engineyard-serverside 2.0.4 → 2.0.5.pre

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 (78) hide show
  1. data/lib/engineyard-serverside.rb +2 -1
  2. data/lib/engineyard-serverside/about.rb +37 -0
  3. data/lib/engineyard-serverside/cli.rb +2 -49
  4. data/lib/engineyard-serverside/configuration.rb +1 -0
  5. data/lib/engineyard-serverside/deploy.rb +5 -8
  6. data/lib/engineyard-serverside/propagator.rb +75 -0
  7. data/lib/engineyard-serverside/server.rb +3 -2
  8. data/lib/engineyard-serverside/servers.rb +28 -14
  9. data/lib/engineyard-serverside/shell.rb +6 -6
  10. data/lib/engineyard-serverside/shell/command_result.rb +1 -1
  11. data/lib/engineyard-serverside/strategies/git.rb +9 -4
  12. data/lib/engineyard-serverside/version.rb +1 -1
  13. data/spec/propagator_spec.rb +95 -0
  14. data/spec/spec_helper.rb +1 -1
  15. metadata +47 -68
  16. data/lib/engineyard-serverside/future.rb +0 -35
  17. data/lib/engineyard-serverside/futures/celluloid.rb +0 -15
  18. data/lib/engineyard-serverside/futures/dataflow.rb +0 -26
  19. data/lib/vendor/celluloid/lib/celluloid.rb +0 -261
  20. data/lib/vendor/celluloid/lib/celluloid/actor.rb +0 -242
  21. data/lib/vendor/celluloid/lib/celluloid/actor_pool.rb +0 -54
  22. data/lib/vendor/celluloid/lib/celluloid/actor_proxy.rb +0 -75
  23. data/lib/vendor/celluloid/lib/celluloid/application.rb +0 -78
  24. data/lib/vendor/celluloid/lib/celluloid/calls.rb +0 -93
  25. data/lib/vendor/celluloid/lib/celluloid/core_ext.rb +0 -14
  26. data/lib/vendor/celluloid/lib/celluloid/events.rb +0 -14
  27. data/lib/vendor/celluloid/lib/celluloid/fiber.rb +0 -33
  28. data/lib/vendor/celluloid/lib/celluloid/fsm.rb +0 -141
  29. data/lib/vendor/celluloid/lib/celluloid/future.rb +0 -60
  30. data/lib/vendor/celluloid/lib/celluloid/links.rb +0 -61
  31. data/lib/vendor/celluloid/lib/celluloid/logger.rb +0 -32
  32. data/lib/vendor/celluloid/lib/celluloid/mailbox.rb +0 -124
  33. data/lib/vendor/celluloid/lib/celluloid/receivers.rb +0 -66
  34. data/lib/vendor/celluloid/lib/celluloid/registry.rb +0 -33
  35. data/lib/vendor/celluloid/lib/celluloid/responses.rb +0 -26
  36. data/lib/vendor/celluloid/lib/celluloid/rspec.rb +0 -2
  37. data/lib/vendor/celluloid/lib/celluloid/signals.rb +0 -50
  38. data/lib/vendor/celluloid/lib/celluloid/supervisor.rb +0 -57
  39. data/lib/vendor/celluloid/lib/celluloid/task.rb +0 -73
  40. data/lib/vendor/celluloid/lib/celluloid/tcp_server.rb +0 -33
  41. data/lib/vendor/celluloid/lib/celluloid/timers.rb +0 -109
  42. data/lib/vendor/celluloid/lib/celluloid/version.rb +0 -4
  43. data/lib/vendor/dataflow/HISTORY +0 -52
  44. data/lib/vendor/dataflow/LICENSE +0 -19
  45. data/lib/vendor/dataflow/README.textile +0 -290
  46. data/lib/vendor/dataflow/Rakefile +0 -36
  47. data/lib/vendor/dataflow/dataflow.rb +0 -124
  48. data/lib/vendor/dataflow/dataflow/actor.rb +0 -22
  49. data/lib/vendor/dataflow/dataflow/equality.rb +0 -44
  50. data/lib/vendor/dataflow/dataflow/future_queue.rb +0 -24
  51. data/lib/vendor/dataflow/dataflow/port.rb +0 -54
  52. data/lib/vendor/dataflow/examples/barrier.rb +0 -9
  53. data/lib/vendor/dataflow/examples/data_driven.rb +0 -17
  54. data/lib/vendor/dataflow/examples/dataflow_http_gets.rb +0 -13
  55. data/lib/vendor/dataflow/examples/flow.rb +0 -20
  56. data/lib/vendor/dataflow/examples/future_http_gets.rb +0 -12
  57. data/lib/vendor/dataflow/examples/future_queue.rb +0 -11
  58. data/lib/vendor/dataflow/examples/instance_variables.rb +0 -15
  59. data/lib/vendor/dataflow/examples/laziness.rb +0 -9
  60. data/lib/vendor/dataflow/examples/local_variables.rb +0 -11
  61. data/lib/vendor/dataflow/examples/messages.rb +0 -26
  62. data/lib/vendor/dataflow/examples/port_http_gets.rb +0 -13
  63. data/lib/vendor/dataflow/examples/port_send.rb +0 -10
  64. data/lib/vendor/dataflow/examples/ring.rb +0 -21
  65. data/lib/vendor/dataflow/spec/actor_spec.rb +0 -28
  66. data/lib/vendor/dataflow/spec/anonymous_variables_spec.rb +0 -21
  67. data/lib/vendor/dataflow/spec/barrier_spec.rb +0 -25
  68. data/lib/vendor/dataflow/spec/by_need_spec.rb +0 -55
  69. data/lib/vendor/dataflow/spec/dataflow_spec.rb +0 -151
  70. data/lib/vendor/dataflow/spec/equality_spec.rb +0 -40
  71. data/lib/vendor/dataflow/spec/flow_spec.rb +0 -25
  72. data/lib/vendor/dataflow/spec/forker_spec.rb +0 -28
  73. data/lib/vendor/dataflow/spec/future_queue_spec.rb +0 -31
  74. data/lib/vendor/dataflow/spec/inspect_spec.rb +0 -19
  75. data/lib/vendor/dataflow/spec/need_later_spec.rb +0 -12
  76. data/lib/vendor/dataflow/spec/port_spec.rb +0 -26
  77. data/lib/vendor/dataflow/spec/spec.opts +0 -1
  78. data/lib/vendor/dataflow/spec/spec_helper.rb +0 -10
@@ -16,6 +16,7 @@ require 'escape'
16
16
  require 'json'
17
17
 
18
18
  require 'engineyard-serverside/version'
19
+ require 'engineyard-serverside/about'
19
20
  require 'engineyard-serverside/strategies/git'
20
21
  require 'engineyard-serverside/task'
21
22
  require 'engineyard-serverside/server'
@@ -25,8 +26,8 @@ require 'engineyard-serverside/lockfile_parser'
25
26
  require 'engineyard-serverside/cli'
26
27
  require 'engineyard-serverside/configuration'
27
28
  require 'engineyard-serverside/deprecation'
28
- require 'engineyard-serverside/future'
29
29
  require 'engineyard-serverside/shell'
30
+ require 'engineyard-serverside/propagator'
30
31
 
31
32
 
32
33
  module EY
@@ -0,0 +1,37 @@
1
+
2
+ module EY
3
+ module Serverside
4
+ module About
5
+ extend self
6
+
7
+ def gem_name
8
+ "engineyard-serverside"
9
+ end
10
+
11
+ def version
12
+ EY::Serverside::VERSION
13
+ end
14
+
15
+ def name_with_version
16
+ "#{gem_name} #{version}"
17
+ end
18
+
19
+ def gem_filename
20
+ "#{gem_name}-#{version}.gem"
21
+ end
22
+
23
+ def gem_file
24
+ File.join(Gem.dir, 'cache', gem_filename)
25
+ end
26
+
27
+ def gem_binary
28
+ File.join(Gem.default_bindir, 'gem')
29
+ end
30
+
31
+ def binary
32
+ File.expand_path("../../../bin/#{gem_name}", __FILE__)
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -136,57 +136,10 @@ module EY
136
136
 
137
137
  private
138
138
 
139
- # Put the same engineyard-serverside on all the servers (Used to be public but is unused as an actual CLI command now)
140
- def propagate(servers, config, shell)
141
- gem_filename = "engineyard-serverside-#{EY::Serverside::VERSION}.gem"
142
- local_gem_file = File.join(Gem.dir, 'cache', gem_filename)
143
- remote_gem_file = File.join(Dir.tmpdir, gem_filename)
144
- gem_binary = File.join(Gem.default_bindir, 'gem')
145
-
146
- servers = servers.remote
147
-
148
- return if servers.empty?
149
-
150
- shell.status "Propagating engineyard-serverside #{EY::Serverside::VERSION} to #{servers.size} server#{servers.size == 1 ? '' : 's' }."
151
-
152
- commands = servers.map do |server|
153
- shell.debug "Building propagate commands for #{server.hostname}"
154
-
155
- egrep_escaped_version = EY::Serverside::VERSION.gsub(/\./, '\.')
156
- # the [,)] is to stop us from looking for e.g. 0.5.1, seeing
157
- # 0.5.11, and mistakenly thinking 0.5.1 is there
158
- has_gem_cmd = "#{gem_binary} list engineyard-serverside | grep \"engineyard-serverside\" | egrep -q '#{egrep_escaped_version}[,)]'"
159
-
160
- proc do
161
- exists = shell.logged_system(server.command_on_server('sh -l -c', has_gem_cmd))
162
- if exists.success?
163
- exists # Future expects logged system result object
164
- else # doesn't have this exact version
165
- shell.status "Installing engineyard-serverside on #{server.hostname}"
166
-
167
- shell.logged_system(Escape.shell_command([
168
- 'scp', '-i', config.paths.internal_key.to_s,
169
- "-o", "StrictHostKeyChecking=no",
170
- local_gem_file,
171
- "#{config.user}@#{server.hostname}:#{remote_gem_file}",
172
- ]))
173
- install_gem_cmd = "#{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'"
174
- shell.logged_system(server.command_on_server('sudo sh -l -c', install_gem_cmd))
175
- end
176
- end
177
- end
178
-
179
- futures = EY::Serverside::Future.call(commands)
180
- unless EY::Serverside::Future.success?(futures)
181
- failures = futures.select {|f| f.error? }.map {|f| f.inspect}.join("\n")
182
- raise EY::Serverside::RemoteFailure.new(failures)
183
- end
184
- end
185
-
186
139
  def init_and_propagate(*args)
187
140
  config, shell = init(*args)
188
141
  servers = load_servers(config)
189
- propagate(servers, config, shell)
142
+ Propagator.call(servers, config, shell)
190
143
  [servers, config, shell]
191
144
  end
192
145
 
@@ -196,7 +149,7 @@ module EY
196
149
  :verbose => config.verbose,
197
150
  :log_path => File.join(ENV['HOME'], "#{config.app}-#{action}.log")
198
151
  )
199
- shell.debug "Initializing engineyard-serverside #{EY::Serverside::VERSION}."
152
+ shell.debug "Initializing #{About.name_with_version}."
200
153
  [config, shell]
201
154
  end
202
155
 
@@ -62,6 +62,7 @@ module EY
62
62
  def_option :branch, 'master'
63
63
  def_option :bundle_without, 'test development'
64
64
  def_option :current_roles, []
65
+ def_option :current_name, nil
65
66
  def_option :asset_roles, [:app_master, :app, :solo]
66
67
  def_option :copy_exclude, []
67
68
  def_option(:user) { ENV['USER'] }
@@ -153,12 +153,10 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
153
153
  # task
154
154
  def push_code
155
155
  shell.status "Pushing code to all servers"
156
- commands = servers.remote.map do |server|
156
+ servers.remote.run_on_each do |server|
157
157
  cmd = server.sync_directory_command(paths.repository_cache)
158
- proc { shell.logged_system(cmd) }
158
+ shell.logged_system(cmd)
159
159
  end
160
- futures = EY::Serverside::Future.call(commands)
161
- EY::Serverside::Future.success?(futures)
162
160
  end
163
161
 
164
162
  # task
@@ -415,7 +413,7 @@ WRAP
415
413
  end
416
414
 
417
415
  def base_callback_command_for(what)
418
- cmd = [serverside_bin, 'hook', what.to_s]
416
+ cmd = [About.binary, 'hook', what.to_s]
419
417
  cmd << '--app' << config.app
420
418
  cmd << '--environment-name' << config.environment_name
421
419
  cmd << '--account-name' << config.account_name
@@ -426,8 +424,7 @@ WRAP
426
424
  end
427
425
 
428
426
  def serverside_bin
429
- basedir = File.expand_path('../../..', __FILE__)
430
- File.join(basedir, 'bin', 'engineyard-serverside')
427
+ About.binary
431
428
  end
432
429
 
433
430
  def puts_deploy_failure
@@ -497,7 +494,7 @@ WRAP
497
494
  clean_bundle_on_system_version_change
498
495
 
499
496
  bundler_version, install_switches = bundler_config
500
- sudo "#{clean_environment} && #{serverside_bin} install_bundler #{bundler_version}"
497
+ sudo "#{clean_environment} && #{About.binary} install_bundler #{bundler_version}"
501
498
  run "#{clean_environment} && cd #{paths.active_release} && ruby -S bundle _#{bundler_version}_ install #{install_switches}"
502
499
 
503
500
  write_system_version
@@ -0,0 +1,75 @@
1
+ require 'engineyard-serverside/about'
2
+
3
+ module EY
4
+ module Serverside
5
+ # Put the same engineyard-serverside on all the servers
6
+ class Propagator
7
+ def self.call(*args)
8
+ new(*args).call
9
+ end
10
+
11
+ attr_reader :config, :shell
12
+
13
+ def initialize(servers, config, shell, options={})
14
+ @remote_servers = servers.remote
15
+ @config, @shell = config, shell
16
+ end
17
+
18
+ # servers that need to have the gem installed
19
+ def servers
20
+ @servers ||= find_servers_missing_gem
21
+ end
22
+
23
+ # Find the servers that need the gem, then install the gem on them
24
+ def call
25
+ servers.any? && propagate
26
+ end
27
+
28
+ def remote_gem_file
29
+ File.join(Dir.tmpdir, About.gem_filename)
30
+ end
31
+
32
+ def gem_binary
33
+ File.join(Gem.default_bindir, 'gem')
34
+ end
35
+
36
+ # the [,)] is to stop us from looking for e.g. 0.5.1, seeing
37
+ # 0.5.11, and mistakenly thinking 0.5.1 is there
38
+ def check_command
39
+ %{#{gem_binary} list #{About.gem_name} | grep "#{About.gem_name}" | egrep -q "#{About.version.gsub(/\./, '\.')}[,)]"}
40
+ end
41
+
42
+ def scp_command(server)
43
+ Escape.shell_command([
44
+ 'scp',
45
+ '-i', config.paths.internal_key.to_s,
46
+ "-o", "StrictHostKeyChecking=no",
47
+ About.gem_file,
48
+ "#{server.authority}:#{remote_gem_file}",
49
+ ])
50
+ end
51
+
52
+ def install_command
53
+ "#{gem_binary} install --no-rdoc --no-ri '#{remote_gem_file}'"
54
+ end
55
+
56
+ def count_servers(set)
57
+ "#{set.size} server#{set.size == 1 ? '' : 's'}"
58
+ end
59
+
60
+ def find_servers_missing_gem
61
+ return @remote_servers if @remote_servers.empty?
62
+ shell.status "Verifying #{About.name_with_version} on #{count_servers(@remote_servers)}."
63
+ @remote_servers.select_in_parallel do |server|
64
+ !shell.logged_system(server.command_on_server('sh -l -c', check_command)).success?
65
+ end
66
+ end
67
+
68
+ def propagate
69
+ shell.status "Propagating #{About.name_with_version} to #{count_servers(servers)}."
70
+ servers.run_on_each { |server| shell.logged_system(scp_command(server)) }
71
+ servers.run_on_each { |server| shell.logged_system(server.command_on_server('sudo sh -l -c', install_command)) }
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,4 +1,5 @@
1
1
  require 'set'
2
+ require 'tempfile'
2
3
 
3
4
  module EY
4
5
  module Serverside
@@ -7,8 +8,8 @@ module EY
7
8
  new(server_hash[:hostname], Set.new(server_hash[:roles].map{|r|r.to_sym}), server_hash[:name], server_hash[:user])
8
9
  end
9
10
 
10
- def initialize(*fields)
11
- super
11
+ def authority
12
+ "#{user}@#{hostname}"
12
13
  end
13
14
 
14
15
  def role
@@ -63,27 +63,41 @@ module EY
63
63
  end
64
64
 
65
65
  # Run a command on this set of servers.
66
- def run(shell, cmd, &blk)
67
- run_on_servers(shell, 'sh -l -c', cmd, &blk)
66
+ def run(shell, cmd, &block)
67
+ run_on_each do |server|
68
+ exec_cmd = server.command_on_server('sh -l -c', cmd, &block)
69
+ shell.logged_system(exec_cmd)
70
+ end
68
71
  end
69
72
 
70
73
  # Run a sudo command on this set of servers.
71
- def sudo(shell, cmd, &blk)
72
- run_on_servers(shell, 'sudo sh -l -c', cmd, &blk)
74
+ def sudo(shell, cmd, &block)
75
+ run_on_each do |server|
76
+ exec_cmd = server.command_on_server('sudo sh -l -c', cmd, &block)
77
+ shell.logged_system(exec_cmd)
78
+ end
73
79
  end
74
80
 
75
- private
76
-
77
- def run_on_servers(shell, prefix, cmd, &block)
78
- commands = map do |server|
79
- exec_cmd = server.command_on_server(prefix, cmd, &block)
80
- proc { shell.logged_system(exec_cmd) }
81
- end
81
+ # Makes a thread for each server and executes the block,
82
+ # returning an array of return values
83
+ def map_in_parallel(&block)
84
+ threads = map { |server| Thread.new { block.call(server) } }
85
+ threads.map { |t| t.value }
86
+ end
82
87
 
83
- futures = EY::Serverside::Future.call(commands)
88
+ def select_in_parallel(&block)
89
+ results = map_in_parallel { |server| block.call(server) ? server : nil }.compact
90
+ self.class.new results
91
+ end
84
92
 
85
- unless EY::Serverside::Future.success?(futures)
86
- failures = futures.select {|f| f.error? }.map {|f| f.inspect}.join("\n")
93
+ # Makes a theard for each server and executes the block,
94
+ # Assumes that the return value of the block is a CommandResult
95
+ # and ensures that all the command results were successful.
96
+ def run_on_each(&block)
97
+ results = map_in_parallel(&block)
98
+ failures = results.reject {|result| result.success? }
99
+ if failures.any?
100
+ message = failures.map { |f| f.inspect }.join("\n")
87
101
  raise EY::Serverside::RemoteFailure.new(failures)
88
102
  end
89
103
  end
@@ -51,15 +51,15 @@ module EY
51
51
  # $ cmd blah do \
52
52
  # > something more
53
53
  # > end
54
- def show_command(cmd)
55
- debug cmd.gsub(/^/, ' > ').sub(/>/, '$')
56
- end
54
+ def command_show(cmd) debug cmd.gsub(/^/,' > ').sub(/>/, '$') end
55
+ def command_out(msg) debug msg.gsub(/^/,' ') end
56
+ def command_err(msg) unknown msg.gsub(/^/,' ') end
57
57
 
58
58
  def logged_system(cmd)
59
- show_command(cmd)
59
+ command_show(cmd)
60
60
  output = ""
61
- outio = YieldIO.new { |msg| output << msg; debug msg.gsub(/^/,' ') }
62
- errio = YieldIO.new { |msg| output << msg; unknown msg.gsub(/^/,' ') }
61
+ outio = YieldIO.new { |msg| output << msg; command_out(msg) }
62
+ errio = YieldIO.new { |msg| output << msg; command_err(msg) }
63
63
  result = spawn_process(cmd, outio, errio)
64
64
  CommandResult.new(cmd, result.exitstatus, output)
65
65
  end
@@ -3,7 +3,7 @@ module EY
3
3
  class Shell
4
4
  class CommandResult < Struct.new(:command, :exitstatus, :output)
5
5
  def success?
6
- exitstatus.zero?
6
+ exitstatus.to_i == 0
7
7
  end
8
8
 
9
9
  def inspect
@@ -31,8 +31,8 @@ module EY
31
31
  def checkout
32
32
  shell.status "Deploying revision #{short_log_message(to_checkout)}"
33
33
  in_repository_cache do
34
- (run("git checkout -q '#{to_checkout}'") ||
35
- run("git reset -q --hard '#{to_checkout}'")) &&
34
+ (run("git checkout -f '#{to_checkout}'") ||
35
+ run("git reset --hard '#{to_checkout}'")) &&
36
36
  run("git submodule sync") &&
37
37
  run("git submodule update --init") &&
38
38
  run("git clean -dfq")
@@ -42,7 +42,12 @@ module EY
42
42
  def to_checkout
43
43
  return @to_checkout if @opts_ref == opts[:ref]
44
44
  @opts_ref = opts[:ref]
45
- @to_checkout = branch?(@opts_ref) ? "origin/#{@opts_ref}" : @opts_ref
45
+ clean_local_branch(@opts_ref)
46
+ @to_checkout = remote_branch?(@opts_ref) ? "origin/#{@opts_ref}" : @opts_ref
47
+ end
48
+
49
+ def clean_local_branch(ref)
50
+ system("#{git} show-branch #{ref} > /dev/null 2>&1 && #{git} branch -D #{ref} > /dev/null 2>&1")
46
51
  end
47
52
 
48
53
  def gc_repository_cache
@@ -79,7 +84,7 @@ module EY
79
84
  "git --git-dir #{repository_cache}/.git --work-tree #{repository_cache}"
80
85
  end
81
86
 
82
- def branch?(ref)
87
+ def remote_branch?(ref)
83
88
  system("#{git} show-branch origin/#{ref} > /dev/null 2>&1")
84
89
  end
85
90
  end
@@ -1,5 +1,5 @@
1
1
  module EY
2
2
  module Serverside
3
- VERSION = '2.0.4'
3
+ VERSION = '2.0.5.pre'
4
4
  end
5
5
  end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe EY::Serverside::Propagator do
4
+ let(:config) do
5
+ EY::Serverside::Deploy::Configuration.new({
6
+ 'app' => 'app',
7
+ 'deploy_to' => deploy_dir.to_s,
8
+ 'user' => ENV['USER'],
9
+ })
10
+ end
11
+
12
+ let(:servers) do
13
+ EY::Serverside::Servers.from_hashes(
14
+ [
15
+ {:user => config.user, :hostname => 'localhost', :roles => %w[solo]},
16
+ {:user => config.user, :hostname => '127.0.0.1', :roles => %w[util], :name => 'myutil'},
17
+ ]
18
+ )
19
+ end
20
+
21
+ let(:solo) { servers.roles(:solo).first }
22
+ let(:util) { servers.roles(:util).first }
23
+
24
+ let(:shell) { mock('shell') }
25
+ let(:check_command) { util.command_on_server('sh -l -c', subject.check_command) }
26
+ let(:scp_command) { subject.scp_command(util) }
27
+ let(:install_command) { util.command_on_server('sudo sh -l -c', subject.install_command) }
28
+
29
+ subject do
30
+ EY::Serverside::Propagator.new(servers, config, shell)
31
+ end
32
+
33
+ def stub_shell_command(command, success, output="STUB OUTPUT")
34
+ shell.should_receive(:logged_system).once.ordered.with(command).and_return do
35
+ test_shell.command_show(command)
36
+ EY::Serverside::Shell::CommandResult.new(command, success ? 0 : 1, output)
37
+ end
38
+ end
39
+
40
+ before do
41
+ shell.stub(:status) do |msg|
42
+ test_shell.status(msg) # hax so you can see the output with VERBOSE
43
+ end
44
+ end
45
+
46
+ context "no remote servers" do
47
+ subject { EY::Serverside::Propagator.new(test_servers, config, shell) }
48
+
49
+ it "returns without doing anything" do
50
+ shell.should_not_receive(:logged_system)
51
+ subject.call
52
+ end
53
+ end
54
+
55
+ context "all servers have the gem" do
56
+ before do
57
+ stub_shell_command(check_command, true)
58
+ shell.should_not_receive(:logged_system).with(scp_command)
59
+ shell.should_not_receive(:logged_system).with(install_command)
60
+ end
61
+
62
+ it "finds no servers" do
63
+ subject.servers.should be_empty
64
+ subject.call
65
+ end
66
+ end
67
+
68
+ context "with a server missing the gem" do
69
+ before do
70
+ stub_shell_command(check_command, false)
71
+ end
72
+
73
+ it "finds servers that need the gem installed and installs it" do
74
+ stub_shell_command(scp_command, true)
75
+ stub_shell_command(install_command, true)
76
+
77
+ subject.servers.to_a.should == [util]
78
+ subject.call
79
+ end
80
+
81
+ it "raises if the scp fails" do
82
+ stub_shell_command(scp_command, false)
83
+ shell.should_not_receive(:logged_system).with(install_command)
84
+
85
+ lambda { subject.call }.should raise_error(EY::Serverside::RemoteFailure)
86
+ end
87
+
88
+ it "raises if the install fails" do
89
+ stub_shell_command(scp_command, true)
90
+ stub_shell_command(install_command, false)
91
+
92
+ lambda { subject.call }.should raise_error(EY::Serverside::RemoteFailure)
93
+ end
94
+ end
95
+ end