engineyard-serverside 1.5.26 → 1.5.27.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/lib/engineyard-serverside.rb +1 -2
  2. data/lib/engineyard-serverside/cli.rb +19 -35
  3. data/lib/engineyard-serverside/deploy.rb +4 -3
  4. data/lib/engineyard-serverside/future.rb +29 -0
  5. data/lib/engineyard-serverside/futures/celluloid.rb +25 -0
  6. data/lib/engineyard-serverside/futures/dataflow.rb +31 -0
  7. data/lib/engineyard-serverside/server.rb +27 -2
  8. data/lib/engineyard-serverside/task.rb +10 -11
  9. data/lib/engineyard-serverside/version.rb +1 -1
  10. data/lib/vendor/celluloid/lib/celluloid.rb +261 -0
  11. data/lib/vendor/celluloid/lib/celluloid/actor.rb +242 -0
  12. data/lib/vendor/celluloid/lib/celluloid/actor_pool.rb +54 -0
  13. data/lib/vendor/celluloid/lib/celluloid/actor_proxy.rb +75 -0
  14. data/lib/vendor/celluloid/lib/celluloid/application.rb +78 -0
  15. data/lib/vendor/celluloid/lib/celluloid/calls.rb +94 -0
  16. data/lib/vendor/celluloid/lib/celluloid/core_ext.rb +14 -0
  17. data/lib/vendor/celluloid/lib/celluloid/events.rb +14 -0
  18. data/lib/vendor/celluloid/lib/celluloid/fiber.rb +33 -0
  19. data/lib/vendor/celluloid/lib/celluloid/fsm.rb +141 -0
  20. data/lib/vendor/celluloid/lib/celluloid/future.rb +60 -0
  21. data/lib/vendor/celluloid/lib/celluloid/links.rb +61 -0
  22. data/lib/vendor/celluloid/lib/celluloid/logger.rb +32 -0
  23. data/lib/vendor/celluloid/lib/celluloid/mailbox.rb +124 -0
  24. data/lib/vendor/celluloid/lib/celluloid/receivers.rb +66 -0
  25. data/lib/vendor/celluloid/lib/celluloid/registry.rb +33 -0
  26. data/lib/vendor/celluloid/lib/celluloid/responses.rb +26 -0
  27. data/lib/vendor/celluloid/lib/celluloid/rspec.rb +2 -0
  28. data/lib/vendor/celluloid/lib/celluloid/signals.rb +50 -0
  29. data/lib/vendor/celluloid/lib/celluloid/supervisor.rb +57 -0
  30. data/lib/vendor/celluloid/lib/celluloid/task.rb +73 -0
  31. data/lib/vendor/celluloid/lib/celluloid/tcp_server.rb +33 -0
  32. data/lib/vendor/celluloid/lib/celluloid/timers.rb +109 -0
  33. data/lib/vendor/celluloid/lib/celluloid/version.rb +4 -0
  34. data/spec/basic_deploy_spec.rb +1 -1
  35. data/spec/bundler_deploy_spec.rb +1 -1
  36. data/spec/nodejs_deploy_spec.rb +1 -1
  37. data/spec/rails31_deploy_spec.rb +1 -1
  38. data/spec/services_deploy_spec.rb +1 -1
  39. metadata +78 -87
@@ -11,11 +11,9 @@ $LOAD_PATH.unshift File.expand_path('vendor/thor/lib', File.dirname(__FILE__))
11
11
  $LOAD_PATH.unshift File.expand_path('vendor/open4/lib', File.dirname(__FILE__))
12
12
  $LOAD_PATH.unshift File.expand_path('vendor/escape/lib', File.dirname(__FILE__))
13
13
  $LOAD_PATH.unshift File.expand_path('vendor/json_pure/lib', File.dirname(__FILE__))
14
- $LOAD_PATH.unshift File.expand_path('vendor/dataflow', File.dirname(__FILE__))
15
14
 
16
15
  require 'escape'
17
16
  require 'json'
18
- require 'dataflow'
19
17
 
20
18
  require 'engineyard-serverside/version'
21
19
  require 'engineyard-serverside/strategies/git'
@@ -27,6 +25,7 @@ require 'engineyard-serverside/lockfile_parser'
27
25
  require 'engineyard-serverside/cli'
28
26
  require 'engineyard-serverside/configuration'
29
27
  require 'engineyard-serverside/deprecation'
28
+ require 'engineyard-serverside/future'
30
29
 
31
30
  module EY
32
31
  module Serverside
@@ -4,13 +4,6 @@ require 'pathname'
4
4
  module EY
5
5
  module Serverside
6
6
  class CLI < Thor
7
- include Dataflow
8
-
9
- def self.start(*)
10
- super
11
- rescue RemoteFailure
12
- exit(1)
13
- end
14
7
 
15
8
  method_option :migrate, :type => :string,
16
9
  :desc => "Run migrations with this deploy",
@@ -63,7 +56,7 @@ module EY
63
56
  EY::Serverside::LoggedOutput.verbose = options[:verbose]
64
57
  EY::Serverside::LoggedOutput.logfile = File.join(ENV['HOME'], "#{options[:app]}-deploy.log")
65
58
 
66
- invoke :propagate
59
+ propagate
67
60
 
68
61
  EY::Serverside::Deploy.new(config).send(default_task)
69
62
  end
@@ -144,7 +137,7 @@ module EY
144
137
 
145
138
  EY::Serverside::Server.load_all_from_array(assemble_instance_hashes(config))
146
139
 
147
- invoke :propagate
140
+ propagate
148
141
 
149
142
  EY::Serverside::Server.all.each do |server|
150
143
  server.sync_directory app_dir
@@ -190,7 +183,7 @@ module EY
190
183
  config = EY::Serverside::Deploy::Configuration.new(options)
191
184
  EY::Serverside::Server.load_all_from_array(assemble_instance_hashes(config))
192
185
 
193
- invoke :propagate
186
+ propagate
194
187
 
195
188
  EY::Serverside::Deploy.new(config).restart_with_maintenance_page
196
189
  end
@@ -212,34 +205,25 @@ module EY
212
205
 
213
206
  desc "propagate", "Propagate the engineyard-serverside gem to the other instances in the cluster. This will install exactly version #{EY::Serverside::VERSION}."
214
207
  def propagate
215
- config = EY::Serverside::Deploy::Configuration.new
216
- gem_filename = "engineyard-serverside-#{EY::Serverside::VERSION}.gem"
208
+ name = "engineyard-serverside"
209
+ version = EY::Serverside::VERSION
210
+ gem_filename = "#{name}-#{version}.gem"
217
211
  local_gem_file = File.join(Gem.dir, 'cache', gem_filename)
218
212
  remote_gem_file = File.join(Dir.tmpdir, gem_filename)
219
- gem_binary = File.join(Gem.default_bindir, 'gem')
220
-
221
- barrier(*(EY::Serverside::Server.all.find_all do |server|
222
- !server.local? # of course this machine has it
223
- end.map do |server|
224
- need_later do
225
- egrep_escaped_version = EY::Serverside::VERSION.gsub(/\./, '\.')
226
- # the [,)] is to stop us from looking for e.g. 0.5.1, seeing
227
- # 0.5.11, and mistakenly thinking 0.5.1 is there
228
- has_gem_cmd = "#{gem_binary} list engineyard-serverside | grep \"engineyard-serverside\" | egrep -q '#{egrep_escaped_version}[,)]'"
229
-
230
- if !server.run(has_gem_cmd) # doesn't have this exact version
231
- puts "~> Installing engineyard-serverside on #{server.hostname}"
232
-
233
- system(Escape.shell_command([
234
- 'scp', '-i', "#{ENV['HOME']}/.ssh/internal",
235
- "-o", "StrictHostKeyChecking=no",
236
- local_gem_file,
237
- "#{config.user}@#{server.hostname}:#{remote_gem_file}",
238
- ]))
239
- server.run("sudo #{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'")
240
- end
213
+
214
+ servers = EY::Serverside::Server.all.find_all { |server| !server.local? }
215
+
216
+ futures = EY::Serverside::Future.call(servers) do |server|
217
+ installed = server.gem?(name, version)
218
+ unless installed
219
+ info "~> Installing #{name} on #{server.hostname}"
220
+ server.copy(local_gem_file, remote_gem_file)
221
+ installed = server.install_gem(remote_gem_file)
241
222
  end
242
- end))
223
+ installed
224
+ end
225
+
226
+ EY::Serverside::Future.success?(futures)
243
227
  end
244
228
 
245
229
  private
@@ -163,9 +163,10 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
163
163
  # task
164
164
  def push_code
165
165
  info "~> Pushing code to all servers"
166
- barrier *(EY::Serverside::Server.all.map do |server|
167
- need_later { server.sync_directory(config.repository_cache) }
168
- end)
166
+ futures = EY::Serverside::Future.call(EY::Serverside::Server.all) do |server|
167
+ server.sync_directory(config.repository_cache)
168
+ end
169
+ EY::Serverside::Future.success?(futures)
169
170
  end
170
171
 
171
172
  # task
@@ -0,0 +1,29 @@
1
+ module EY
2
+ module Serverside
3
+ class Future
4
+ def self.success?(futures)
5
+ futures.empty? || futures.all? {|f| f.success?}
6
+ end
7
+
8
+ def initialize(server, *args, &block)
9
+ @server = server
10
+ @args = args
11
+ @block = block
12
+ end
13
+
14
+ def success?
15
+ @value == true
16
+ end
17
+
18
+ def error?
19
+ !success?
20
+ end
21
+ end
22
+
23
+ if defined?(Fiber)
24
+ require 'engineyard-serverside/futures/celluloid'
25
+ else
26
+ require 'engineyard-serverside/futures/dataflow'
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ module EY
2
+ module Serverside
3
+ $LOAD_PATH.unshift File.expand_path('../../vendor/celluloid/lib', File.dirname(__FILE__))
4
+ require 'celluloid'
5
+ class Future
6
+ def self.call(servers, *args, &block)
7
+ futures = servers.map do |server|
8
+ new(server, *args, &block)
9
+ end
10
+
11
+ futures.each {|f| f.call}
12
+ futures
13
+ end
14
+
15
+ def future
16
+ Celluloid::Future.new(@server, *@args, &@block)
17
+ end
18
+
19
+ def call
20
+ # Celluloid needs to call the block explicitely
21
+ @value ||= future.call
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ module EY
2
+ module Serverside
3
+ $LOAD_PATH.unshift File.expand_path('../../vendor/dataflow', File.dirname(__FILE__))
4
+ require 'dataflow'
5
+
6
+ class Future
7
+ extend Dataflow
8
+
9
+ def self.call(servers, *args, &block)
10
+ futures = []
11
+ # Dataflow needs to call `barrier` and `need_later` in the same object
12
+ barrier(*servers.map do |server|
13
+ future = new(server, *args, &block)
14
+ futures << future
15
+
16
+ need_later { future.call }
17
+ end)
18
+
19
+ futures
20
+ end
21
+
22
+ def future
23
+ @block.call(@server, *@args)
24
+ end
25
+
26
+ def call
27
+ @value ||= future
28
+ end
29
+ end
30
+ end
31
+ end
@@ -83,12 +83,37 @@ module EY
83
83
  if local?
84
84
  logged_system(command)
85
85
  else
86
- logged_system(ssh_command + " " + Escape.shell_command(["#{user}@#{hostname}", command]))
86
+ logged_system(ssh_command + Escape.shell_command(["#{user}@#{hostname}", command]))
87
87
  end
88
88
  end
89
89
 
90
+ def copy(local_file, remote_file)
91
+ logged_system(scp_command + Escape.shell_command([local_file, "#{user}@#{hostname}:#{remote_file}"]))
92
+ end
93
+
90
94
  def ssh_command
91
- "ssh -i #{ENV['HOME']}/.ssh/internal -o StrictHostKeyChecking=no -o PasswordAuthentication=no"
95
+ "ssh #{ssh_options} "
96
+ end
97
+
98
+ def scp_command
99
+ "scp #{ssh_options} "
100
+ end
101
+
102
+ def ssh_options
103
+ "-i #{ENV['HOME']}/.ssh/internal -o StrictHostKeyChecking=no -o PasswordAuthentication=no"
104
+ end
105
+
106
+ def gem?(name, version)
107
+ run("#{gem_command} list -i #{name} -v '#{version}'")
108
+ end
109
+
110
+ def install_gem(path)
111
+ # resin + ruby 1.8.6 need sudo privileges to install gems
112
+ run("sudo #{gem_command} install -q --no-ri --no-rdoc #{path}")
113
+ end
114
+
115
+ def gem_command
116
+ File.join(Gem.default_bindir, 'gem')
92
117
  end
93
118
 
94
119
  end
@@ -1,7 +1,6 @@
1
1
  module EY
2
2
  module Serverside
3
3
  class Task
4
- include Dataflow
5
4
 
6
5
  attr_reader :config
7
6
  alias :c :config
@@ -51,18 +50,18 @@ module EY
51
50
 
52
51
  private
53
52
 
54
- def run_on_roles(cmd, wrapper=%w[sh -l -c])
55
- results = EY::Serverside::Server.from_roles(@roles).map do |server|
56
- to_run = block_given? ? yield(server, cmd.dup) : cmd
57
- need_later { server.run(Escape.shell_command(wrapper + [to_run])) }
53
+ def run_on_roles(cmd, wrapper=%w[sh -l -c], &block)
54
+ servers = EY::Serverside::Server.from_roles(@roles)
55
+ futures = EY::Serverside::Future.call(servers, block_given?) do |server, exec_block|
56
+ to_run = exec_block ? block.call(server, cmd.dup) : cmd
57
+ server.run(Escape.shell_command(wrapper + [to_run]))
58
58
  end
59
- barrier *results
60
- # MRI's truthiness check is an internal C thing that does not call
61
- # any methods... so Dataflow cannot proxy it & we must "x == true"
62
- # Rubinius, wherefore art thou!?
63
- results.all?{|x| x == true } || raise(EY::Serverside::RemoteFailure.new(cmd))
64
- end
65
59
 
60
+ unless EY::Serverside::Future.success?(futures)
61
+ failures = futures.select {|f| f.error? }.map {|f| f.inspect}.join("\n")
62
+ raise EY::Serverside::RemoteFailure.new(failures)
63
+ end
64
+ end
66
65
  end
67
66
  end
68
67
  end
@@ -1,5 +1,5 @@
1
1
  module EY
2
2
  module Serverside
3
- VERSION = '1.5.26'
3
+ VERSION = '1.5.27.pre'
4
4
  end
5
5
  end
@@ -0,0 +1,261 @@
1
+ require 'logger'
2
+ require 'thread'
3
+
4
+ module Celluloid
5
+ @logger = Logger.new STDERR
6
+
7
+ class << self
8
+ attr_accessor :logger # Thread-safe logger class
9
+
10
+ def included(klass)
11
+ klass.send :extend, ClassMethods
12
+ end
13
+
14
+ # Are we currently inside of an actor?
15
+ def actor?
16
+ !!Thread.current[:actor]
17
+ end
18
+
19
+ # Obtain the currently running actor (if one exists)
20
+ def current_actor
21
+ actor = Thread.current[:actor]
22
+ raise NotActorError, "not in actor scope" unless actor
23
+ actor.proxy
24
+ end
25
+ alias_method :current, :current_actor
26
+
27
+ # Receive an asynchronous message
28
+ def receive(timeout = nil, &block)
29
+ actor = Thread.current[:actor]
30
+ if actor
31
+ actor.receive(timeout, &block)
32
+ else
33
+ Thread.mailbox.receive(timeout, &block)
34
+ end
35
+ end
36
+
37
+ # Sleep letting the actor continue processing messages
38
+ def sleep(interval)
39
+ actor = Thread.current[:actor]
40
+ if actor
41
+ actor.sleep(interval)
42
+ else
43
+ Kernel.sleep interval
44
+ end
45
+ end
46
+
47
+ # Obtain a hash of active tasks to their current activities
48
+ def tasks
49
+ actor = Thread.current[:actor]
50
+ raise NotActorError, "not in actor scope" unless actor
51
+ actor.tasks
52
+ end
53
+ end
54
+
55
+ # Class methods added to classes which include Celluloid
56
+ module ClassMethods
57
+ # Create a new actor
58
+ def new(*args, &block)
59
+ proxy = Actor.new(allocate).proxy
60
+ proxy.send(:initialize, *args, &block)
61
+ proxy
62
+ end
63
+ alias_method :spawn, :new
64
+
65
+ # Create a new actor and link to the current one
66
+ def new_link(*args, &block)
67
+ current_actor = Celluloid.current_actor
68
+ raise NotActorError, "can't link outside actor context" unless current_actor
69
+
70
+ proxy = Actor.new(allocate).proxy
71
+ current_actor.link proxy
72
+ proxy.send(:initialize, *args, &block)
73
+ proxy
74
+ end
75
+ alias_method :spawn_link, :new_link
76
+
77
+ # Create a supervisor which ensures an instance of an actor will restart
78
+ # an actor if it fails
79
+ def supervise(*args, &block)
80
+ Supervisor.supervise(self, *args, &block)
81
+ end
82
+
83
+ # Create a supervisor which ensures an instance of an actor will restart
84
+ # an actor if it fails, and keep the actor registered under a given name
85
+ def supervise_as(name, *args, &block)
86
+ Supervisor.supervise_as(name, self, *args, &block)
87
+ end
88
+
89
+ # Trap errors from actors we're linked to when they exit
90
+ def trap_exit(callback)
91
+ @exit_handler = callback.to_sym
92
+ end
93
+
94
+ # Obtain the exit handler for this actor
95
+ attr_reader :exit_handler
96
+
97
+ # Configure a custom mailbox factory
98
+ def use_mailbox(klass = nil, &block)
99
+ if block
100
+ define_method(:mailbox_factory, &block)
101
+ else
102
+ define_method(:mailbox_factory) { klass.new }
103
+ end
104
+ end
105
+ end
106
+
107
+ #
108
+ # Instance methods
109
+ #
110
+
111
+ # Is this actor alive?
112
+ def alive?
113
+ Thread.current[:actor].alive?
114
+ end
115
+
116
+ # Raise an exception in caller context, but stay running
117
+ def abort(cause)
118
+ raise AbortError.new(cause)
119
+ end
120
+
121
+ # Terminate this actor
122
+ def terminate
123
+ Thread.current[:actor].terminate
124
+ end
125
+
126
+ def inspect
127
+ str = "#<Celluloid::Actor(#{self.class}:0x#{object_id.to_s(16)})"
128
+ ivars = instance_variables.map do |ivar|
129
+ "#{ivar}=#{instance_variable_get(ivar).inspect}"
130
+ end
131
+
132
+ str << " " << ivars.join(' ') unless ivars.empty?
133
+ str << ">"
134
+ end
135
+
136
+ # Send a signal with the given name to all waiting methods
137
+ def signal(name, value = nil)
138
+ Thread.current[:actor].signal name, value
139
+ end
140
+
141
+ # Wait for the given signal
142
+ def wait(name)
143
+ Thread.current[:actor].wait name
144
+ end
145
+
146
+ # Obtain the current_actor
147
+ def current_actor
148
+ Celluloid.current_actor
149
+ end
150
+
151
+ # Obtain the running tasks for this actor
152
+ def tasks
153
+ Celluloid.tasks
154
+ end
155
+
156
+ # Obtain the Ruby object the actor is wrapping. This should ONLY be used
157
+ # for a limited set of use cases like runtime metaprogramming. Interacting
158
+ # directly with the wrapped object foregoes any kind of thread safety that
159
+ # Celluloid would ordinarily provide you, and the object is guaranteed to
160
+ # be shared with at least the actor thread. Tread carefully.
161
+ def wrapped_object; self; end
162
+
163
+ # Obtain the Celluloid::Links for this actor
164
+ def links
165
+ Thread.current[:actor].links
166
+ end
167
+
168
+ # Link this actor to another, allowing it to crash or react to errors
169
+ def link(actor)
170
+ actor.notify_link current_actor
171
+ notify_link actor
172
+ end
173
+
174
+ # Remove links to another actor
175
+ def unlink(actor)
176
+ actor.notify_unlink current_actor
177
+ notify_unlink actor
178
+ end
179
+
180
+ def notify_link(actor)
181
+ links << actor
182
+ end
183
+
184
+ def notify_unlink(actor)
185
+ links.delete actor
186
+ end
187
+
188
+ # Is this actor linked to another?
189
+ def linked_to?(actor)
190
+ Thread.current[:actor].links.include? actor
191
+ end
192
+
193
+ # Receive an asynchronous message via the actor protocol
194
+ def receive(timeout = nil, &block)
195
+ Celluloid.receive(timeout, &block)
196
+ end
197
+
198
+ # Sleep while letting the actor continue to receive messages
199
+ def sleep(interval)
200
+ Celluloid.sleep(interval)
201
+ end
202
+
203
+ # Call a block after a given interval
204
+ def after(interval, &block)
205
+ Thread.current[:actor].after(interval, &block)
206
+ end
207
+
208
+ # Perform a blocking or computationally intensive action inside an
209
+ # asynchronous thread pool, allowing the caller to continue processing other
210
+ # messages in its mailbox in the meantime
211
+ def async(&block)
212
+ # This implementation relies on the present implementation of
213
+ # Celluloid::Future, which uses an Actor to run the block
214
+ Future.new(&block).value
215
+ end
216
+
217
+ # Process async calls via method_missing
218
+ def method_missing(meth, *args, &block)
219
+ # bang methods are async calls
220
+ if meth.to_s.match(/!$/)
221
+ unbanged_meth = meth.to_s.sub(/!$/, '')
222
+ call = AsyncCall.new(@mailbox, unbanged_meth, args, block)
223
+
224
+ begin
225
+ Thread.current[:actor].mailbox << call
226
+ rescue MailboxError
227
+ # Silently swallow asynchronous calls to dead actors. There's no way
228
+ # to reliably generate DeadActorErrors for async calls, so users of
229
+ # async calls should find other ways to deal with actors dying
230
+ # during an async call (i.e. linking/supervisors)
231
+ end
232
+
233
+ return # casts are async and return immediately
234
+ end
235
+
236
+ super
237
+ end
238
+ end
239
+
240
+ require 'celluloid/version'
241
+ require 'celluloid/actor_proxy'
242
+ require 'celluloid/calls'
243
+ require 'celluloid/core_ext'
244
+ require 'celluloid/events'
245
+ require 'celluloid/fiber'
246
+ require 'celluloid/fsm'
247
+ require 'celluloid/links'
248
+ require 'celluloid/logger'
249
+ require 'celluloid/mailbox'
250
+ require 'celluloid/receivers'
251
+ require 'celluloid/registry'
252
+ require 'celluloid/responses'
253
+ require 'celluloid/signals'
254
+ require 'celluloid/task'
255
+ require 'celluloid/timers'
256
+
257
+ require 'celluloid/actor'
258
+ require 'celluloid/actor_pool'
259
+ require 'celluloid/supervisor'
260
+ require 'celluloid/future'
261
+ require 'celluloid/application'