engineyard-serverside 2.0.4 → 2.0.5.pre

Sign up to get free protection for your applications and to get access to all the features.
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