engineyard-serverside 2.1.4 → 2.2.0.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -76,7 +76,7 @@ module EY
76
76
 
77
77
  private
78
78
  def run(cmd)
79
- shell.logged_system(cmd).success?
79
+ EY::Serverside::Spawner.run(cmd, shell, nil).success?
80
80
  end
81
81
 
82
82
  def in_repository_cache
@@ -77,11 +77,11 @@ Please consider:
77
77
  end
78
78
 
79
79
  def run(cmd, &block)
80
- servers.roles(@roles).run(shell, cmd, &block)
80
+ servers.roles(@roles).run(cmd, &block)
81
81
  end
82
82
 
83
83
  def sudo(cmd, &block)
84
- servers.roles(@roles).sudo(shell, cmd, &block)
84
+ servers.roles(@roles).sudo(cmd, &block)
85
85
  end
86
86
 
87
87
  end
@@ -1,5 +1,5 @@
1
1
  module EY
2
2
  module Serverside
3
- VERSION = '2.1.4'
3
+ VERSION = '2.2.0.pre'
4
4
  end
5
5
  end
@@ -38,7 +38,9 @@ describe "Deploying an application that uses Bundler" do
38
38
  end
39
39
 
40
40
  it "generates bundler binstubs" do
41
- deploy_dir.join('current', 'ey_bundler_binstubs', 'rake').should exist
41
+ pending "doesn't work with mocked bundler" do
42
+ deploy_dir.join('current', 'ey_bundler_binstubs', 'rake').should exist
43
+ end
42
44
  end
43
45
  end
44
46
 
@@ -72,14 +74,14 @@ describe "Deploying an application that uses Bundler" do
72
74
  context "with a failing Gemfile" do
73
75
  before(:all) do
74
76
  begin
75
- deploy_test_application('bundle_fails', :verbose => false)
77
+ deploy_test_application('bundle_fails', 'bundle_install_fails' => true, 'verbose' => false)
76
78
  rescue EY::Serverside::RemoteFailure
77
79
  end
78
80
  end
79
81
 
80
82
  it "prints the failure to the log" do
81
83
  out = read_output
82
- out.should =~ %r|this-gem-does-not-exist-which-makes-bundle-install-fail|
84
+ out.should =~ %r|bundle install failure|
83
85
  deploy_dir.join('current', 'after_bundle.ran' ).should_not exist
84
86
  end
85
87
  end
@@ -89,20 +89,18 @@ describe "deploy hooks" do
89
89
 
90
90
  it "runs things with sudo" do
91
91
  hook = deploy_hook
92
- cmd = "sudo sh -l -c 'do it as root'"
93
- result = EY::Serverside::Shell::CommandResult.new(cmd, 0, "out\nerr", test_servers.first)
94
- hook.callback_context.shell.should_receive(:logged_system).with(cmd).and_return(result)
95
- hook.eval_hook('sudo("do it as root") || raise("failed")')
92
+ mock_sudo do
93
+ hook.eval_hook('sudo("true") || raise("failed")')
94
+ end
96
95
  end
97
96
 
98
97
  it "raises when the bang method alternative is used" do
99
98
  hook = deploy_hook
100
- cmd = "sudo sh -l -c false"
101
- result = EY::Serverside::Shell::CommandResult.new(cmd, 1, "fail", test_servers.first)
102
- hook.callback_context.shell.should_receive(:logged_system).with(cmd).and_return(result)
103
- lambda {
104
- hook.eval_hook('sudo!("false")')
105
- }.should raise_error(RuntimeError)
99
+ mock_sudo do
100
+ lambda {
101
+ hook.eval_hook('sudo!("false")')
102
+ }.should raise_error(RuntimeError)
103
+ end
106
104
  out = read_output
107
105
  out.should =~ %r|FATAL: Exception raised in deploy hook /data/app_name/releases/\d+/deploy/fake_test_hook.rb.|
108
106
  out.should =~ %r|RuntimeError: .*sudo!.*Command failed. false|
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe "the git deploy strategy" do
4
4
  subject do
5
5
  fixtures_dir = Pathname.new(__FILE__).dirname.join("fixtures")
6
- gitrepo_dir = tmpdir.join("gitrepo-#{Time.now.to_i}-#{$$}")
6
+ gitrepo_dir = tmpdir.join("gitrepo-#{Time.now.utc.strftime("%Y%m%d%H%M%S")}#{Time.now.tv_usec}-#{$$}")
7
7
  gitrepo_dir.mkdir
8
8
  system "tar xzf #{fixtures_dir.join('gitrepo.tar.gz')} --strip-components 1 -C #{gitrepo_dir}"
9
9
 
@@ -1,14 +1,15 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe "Deploying an application that uses Node.js and NPM" do
4
- before(:all) do
5
- deploy_test_application('nodejs')
4
+ with_npm_mocked do |mocked|
5
+ before(:all) do
6
+ mock_npm if mocked
7
+ deploy_test_application('nodejs')
8
+ end
9
+
10
+ it "runs 'npm install'" do
11
+ install_cmd = @deployer.commands.grep(/npm install/).first
12
+ install_cmd.should_not be_nil
13
+ end
6
14
  end
7
-
8
- it "runs 'npm install'" do
9
- install_cmd = @deployer.commands.grep(/npm install/).first
10
- install_cmd.should_not be_nil
11
- end
12
- end if $NPM_INSTALLED
13
-
14
-
15
+ end
@@ -2,75 +2,67 @@ require 'spec_helper'
2
2
 
3
3
  describe "Deploying an application that uses PHP and Composer" do
4
4
 
5
- context "with composer available" do
6
-
7
- context "with a composer.lock" do
8
- before(:all) do
9
- deploy_test_application('php_composer_lock')
10
- end
5
+ with_composer_mocked do |mocked|
6
+ context "with composer available" do
7
+ before(:all) { mock_composer if mocked }
8
+ context "with a composer.lock" do
9
+ before(:all) do
10
+ deploy_test_application('php_composer_lock')
11
+ end
11
12
 
12
- it "runs 'composer install'" do
13
- install_cmd = @deployer.commands.grep(/composer install/).first
14
- install_cmd.should_not be_nil
15
- end
13
+ it "runs 'composer install'" do
14
+ install_cmd = @deployer.commands.grep(/composer install/).first
15
+ install_cmd.should_not be_nil
16
+ end
16
17
 
17
- it "runs 'composer self-update' before 'composer install'" do
18
- update_cmd = nil
19
- @deployer.commands.each do |cmd|
20
- update_cmd ||= /composer self-update/.match(cmd)
21
- if /composer install/.match(cmd)
22
- update_cmd.should_not be nil
18
+ it "runs 'composer self-update' before 'composer install'" do
19
+ update_cmd = nil
20
+ @deployer.commands.each do |cmd|
21
+ update_cmd ||= /composer self-update/.match(cmd)
22
+ if /composer install/.match(cmd)
23
+ update_cmd.should_not be nil
24
+ end
23
25
  end
24
26
  end
25
27
  end
26
- end
27
28
 
28
- context "WITHOUT a composer.lock but with composer.json" do
29
- before(:all) do
30
- deploy_test_application('php_no_composer_lock')
31
- end
29
+ context "WITHOUT a composer.lock but with composer.json" do
30
+ before(:all) do
31
+ deploy_test_application('php_no_composer_lock')
32
+ end
32
33
 
33
- it "outputs a warning about deploying without a .lock" do
34
- warning_out = read_output.should include("WARNING: composer.json found but composer.lock missing!")
35
- warning_out.should_not be_nil
36
- end
34
+ it "outputs a warning about deploying without a .lock" do
35
+ warning_out = read_output.should include("WARNING: composer.json found but composer.lock missing!")
36
+ warning_out.should_not be_nil
37
+ end
37
38
 
38
- it "runs 'composer install'" do
39
- install_cmd = @deployer.commands.grep(/composer install/).first
40
- install_cmd.should_not be_nil
41
- end
39
+ it "runs 'composer install'" do
40
+ install_cmd = @deployer.commands.grep(/composer install/).first
41
+ install_cmd.should_not be_nil
42
+ end
42
43
 
43
- it "runs 'composer self-update' before 'composer install'" do
44
- update_cmd = nil
45
- @deployer.commands.each do |cmd|
46
- update_cmd ||= /composer self-update/.match(cmd)
47
- if /composer install/.match(cmd)
48
- update_cmd.should_not be nil
44
+ it "runs 'composer self-update' before 'composer install'" do
45
+ update_cmd = nil
46
+ @deployer.commands.each do |cmd|
47
+ update_cmd ||= /composer self-update/.match(cmd)
48
+ if /composer install/.match(cmd)
49
+ update_cmd.should_not be nil
50
+ end
49
51
  end
50
52
  end
53
+
51
54
  end
52
55
 
53
56
  end
54
57
 
55
- end if $COMPOSER_INSTALLED
56
-
57
- context "without composer available" do
58
-
59
- context "with a composer.lock" do
58
+ context "without composer available" do
60
59
 
61
60
  it "fails to deploy" do
62
61
  expect {deploy_test_application('php_composer_lock')}.to raise_error EY::Serverside::RemoteFailure
63
- end
64
- end
65
-
66
- context "WITHOUT a composer.lock but with composer.json" do
67
-
68
- it "fails to deploy" do
69
62
  expect {deploy_test_application('php_no_composer_lock')}.to raise_error EY::Serverside::RemoteFailure
70
63
  end
71
64
 
72
65
  end
73
-
74
- end if !$COMPOSER_INSTALLED
66
+ end
75
67
  end
76
68
 
data/spec/server_spec.rb CHANGED
@@ -2,11 +2,11 @@ require 'spec_helper'
2
2
 
3
3
  describe EY::Serverside::Server do
4
4
  it "starts off empty" do
5
- EY::Serverside::Servers.new([]).should be_empty
5
+ EY::Serverside::Servers.new([], test_shell).should be_empty
6
6
  end
7
7
 
8
8
  it "loads from hashes" do
9
- servers = EY::Serverside::Servers.from_hashes([{:hostname => 'otherhost', :roles => %w[fire water]}])
9
+ servers = EY::Serverside::Servers.from_hashes([{:hostname => 'otherhost', :roles => %w[fire water]}], test_shell)
10
10
  servers.size.should == 1
11
11
  end
12
12
 
@@ -15,12 +15,12 @@ describe EY::Serverside::Server do
15
15
  EY::Serverside::Servers.from_hashes([
16
16
  {:hostname => 'otherhost', :roles => [:fire]},
17
17
  {:hostname => 'otherhost', :roles => [:water]},
18
- ])
18
+ ], test_shell)
19
19
  end.should raise_error(EY::Serverside::Servers::DuplicateHostname)
20
20
  end
21
21
 
22
22
  it "makes sure your roles are symbols at creation time" do
23
- servers = EY::Serverside::Servers.from_hashes([{:hostname => 'otherhost', :roles => %w[fire water]}])
23
+ servers = EY::Serverside::Servers.from_hashes([{:hostname => 'otherhost', :roles => %w[fire water]}], test_shell)
24
24
  servers.each { |server| server.roles.should == Set[:fire, :water] }
25
25
  end
26
26
 
@@ -30,7 +30,7 @@ describe EY::Serverside::Server do
30
30
  {:hostname => 'localhost', :roles => [:ice, :cold]},
31
31
  {:hostname => 'firewater', :roles => [:fire, :water]},
32
32
  {:hostname => 'icewater', :roles => [:ice, :water]},
33
- ])
33
+ ], test_shell)
34
34
  end
35
35
 
36
36
  it "#roles works with strings or symbols" do
data/spec/spec_helper.rb CHANGED
@@ -20,6 +20,7 @@ require File.expand_path('../support/integration', __FILE__)
20
20
  FIXTURES_DIR = Pathname.new(__FILE__).dirname.join("fixtures")
21
21
  TMPDIR = Pathname.new(__FILE__).dirname.parent.join('tmp')
22
22
  GROUP = `id -gn`.strip
23
+ INTERNAL_KEY = Pathname.new("~/.ssh/id_rsa").expand_path
23
24
 
24
25
  module EY
25
26
  module Serverside
@@ -31,15 +32,35 @@ module EY
31
32
  class Strategies::Git
32
33
  def short_log_message(_) "" end
33
34
  end
35
+
36
+
37
+ class Paths
38
+ # This needs to be patched for the tests to succeed, but
39
+ # the chances of 2 real deploys colliding in the same second
40
+ # is very very low.
41
+ def active_release
42
+ @active_release ||= if Time.now.utc.strftime("%L") =~ /L/ # old ruby
43
+ path(:releases, Time.now.utc.strftime("%Y%m%d%H%M%S#{Time.now.tv_usec}"))
44
+ else
45
+ path(:releases, Time.now.utc.strftime("%Y%m%d%H%M%S%L"))
46
+ end
47
+ end
48
+ end
49
+
34
50
  end
35
51
  end
36
52
 
37
- Spec::Runner.configure do |config|
53
+ module SpecDependencyHelpers
38
54
  $NPM_INSTALLED = system('which npm 2>&1')
39
55
  unless $NPM_INSTALLED
40
56
  $stderr.puts "npm not found; skipping Node.js specs."
41
57
  end
42
58
 
59
+ def with_npm_mocked(&block)
60
+ context("mocked") { yield true }
61
+ context("unmocked") { yield false } if $NPM_INSTALLED
62
+ end
63
+
43
64
  $COMPOSER_INSTALLED = system('command -v composer > /dev/null')
44
65
  if $COMPOSER_INSTALLED
45
66
  $stderr.puts "composer found; skipping tests that expect it to be missing."
@@ -47,6 +68,15 @@ Spec::Runner.configure do |config|
47
68
  $stderr.puts "composer not found; skipping tests that expect it to be available."
48
69
  end
49
70
 
71
+ def with_composer_mocked(&block)
72
+ context("mocked") { yield true }
73
+ context("unmocked") { yield false } if $COMPOSER_INSTALLED
74
+ end
75
+ end
76
+
77
+ RSpec.configure do |config|
78
+ config.extend SpecDependencyHelpers
79
+
50
80
  config.before(:all) do
51
81
  make_tmpdir
52
82
  EY::Serverside.dna_json = MultiJson.dump({})
@@ -111,7 +141,7 @@ Spec::Runner.configure do |config|
111
141
 
112
142
  def test_shell(verbose=true)
113
143
  @test_shell ||= begin
114
- @log_path = tmpdir.join("serverside-deploy-#{Time.now.to_i}-#{$$}.log")
144
+ @log_path = tmpdir.join("serverside-deploy-#{Time.now.to_f}-#{$$}.log")
115
145
  EY::Serverside::Shell.new(:verbose => verbose, :log_path => @log_path, :stdout => stdout, :stderr => stderr)
116
146
  end
117
147
  end
@@ -120,13 +150,87 @@ Spec::Runner.configure do |config|
120
150
  be_exist
121
151
  end
122
152
 
153
+ def bindir
154
+ @bindir ||= begin
155
+ dir = tmpdir.join("ey_test_cmds_#{Time.now.to_f}_#{$$}")
156
+ dir.mkpath
157
+ dir
158
+ end
159
+ end
160
+
161
+ def mock_command(cmd, contents, &block)
162
+ bindir.join(cmd).open('w') do |f|
163
+ f.write contents
164
+ f.chmod(0755)
165
+ end
166
+ with_mocked_commands(&block) if block_given?
167
+ end
168
+
169
+ def mock_bundler(failure = false, &block)
170
+ mock_command('bundle', <<-SCRIPT, &block)
171
+ #!#{`which ruby`}
172
+ puts "Bundling gems"
173
+ $stdout.flush
174
+ #{failure && '$stderr.puts "bundle install failure"; exit 1'}
175
+ SCRIPT
176
+ end
177
+
178
+ def mock_npm(&block)
179
+ mock_command('npm', <<-SCRIPT, &block)
180
+ #!/bin/bash
181
+ echo "Running npm with $@"
182
+ SCRIPT
183
+ end
184
+
185
+ def mock_composer(&block)
186
+ mock_command('composer', <<-SCRIPT, &block)
187
+ #!/bin/bash
188
+ echo "Running composer with $@"
189
+ SCRIPT
190
+ end
191
+
192
+ def mock_sudo(&block)
193
+ mock_command('sudo', <<-SCRIPT, &block)
194
+ #!/bin/bash
195
+ echo "$@"
196
+ exec "$@"
197
+ SCRIPT
198
+ end
199
+
200
+ def with_mocked_commands(&block)
201
+ with_env('PATH' => "#{bindir}:#{ENV['PATH']}", &block)
202
+ end
203
+
204
+ def with_env(new_env_vars)
205
+ raise ArgumentError, "with_env takes a block" unless block_given?
206
+
207
+ old_env_vars = {}
208
+ new_env_vars.each do |k, v|
209
+ if ENV.has_key?(k)
210
+ old_env_vars[k] = ENV[k]
211
+ end
212
+ ENV[k] = v if v
213
+ end
214
+
215
+ yield
216
+ ensure
217
+ new_env_vars.keys.each do |k|
218
+ if old_env_vars.has_key?(k)
219
+ ENV[k] = old_env_vars[k]
220
+ else
221
+ ENV.delete(k)
222
+ end
223
+ end
224
+ end
225
+
226
+
123
227
  def deploy_dir
124
- @deploy_dir ||= tmpdir.join("serverside-deploy-#{Time.now.to_i}-#{$$}")
228
+ @deploy_dir ||= tmpdir.join("serverside-deploy-#{Time.now.to_f}-#{$$}")
125
229
  end
126
230
 
127
231
  # set up EY::Serverside::Server like we're on a solo
128
232
  def test_servers
129
- @test_servers ||= EY::Serverside::Servers.from_hashes([{:hostname => 'localhost', :roles => %w[solo], :user => ENV['USER']}])
233
+ @test_servers ||= EY::Serverside::Servers.from_hashes([{:hostname => 'localhost', :roles => %w[solo], :user => ENV['USER']}], test_shell)
130
234
  end
131
235
 
132
236
  # When a repo fixture name is specified, the files found in the specified
@@ -178,8 +282,12 @@ Spec::Runner.configure do |config|
178
282
 
179
283
  @binpath = File.expand_path(File.join(File.dirname(__FILE__), '..', 'bin', 'engineyard-serverside'))
180
284
  FullTestDeploy.on_create_callback = block
181
- capture do
182
- EY::Serverside::CLI.start(@argv)
285
+
286
+ mock_bundler(options['bundle_install_fails'])
287
+ with_mocked_commands do
288
+ capture do
289
+ EY::Serverside::CLI.start(@argv)
290
+ end
183
291
  end
184
292
  ensure
185
293
  @deployer = EY::Serverside::Deploy.deployer
@@ -188,6 +296,7 @@ Spec::Runner.configure do |config|
188
296
 
189
297
  def redeploy_test_application(extra_config = {}, &block)
190
298
  raise "Please deploy_test_application first" unless @argv
299
+ bundle_install_fails = extra_config.delete('bundle_install_fails')
191
300
 
192
301
  @action = @adapter.deploy do |args|
193
302
  extra_config.each do |key,val|
@@ -202,8 +311,13 @@ Spec::Runner.configure do |config|
202
311
  @argv = @action.commands.last.to_argv[2..-1]
203
312
 
204
313
  FullTestDeploy.on_create_callback = block
205
- capture do
206
- EY::Serverside::CLI.start(@argv)
314
+
315
+ mock_bundler(bundle_install_fails)
316
+
317
+ with_mocked_commands do
318
+ capture do
319
+ EY::Serverside::CLI.start(@argv)
320
+ end
207
321
  end
208
322
  ensure
209
323
  @deployer = EY::Serverside::Deploy.deployer