engineyard 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/engineyard.rb +2 -2
- data/lib/engineyard/api.rb +19 -0
- data/lib/engineyard/cli.rb +24 -12
- data/lib/engineyard/cli/action/deploy.rb +112 -0
- data/lib/engineyard/cli/api.rb +14 -1
- data/lib/engineyard/error.rb +27 -0
- data/lib/engineyard/model.rb +9 -0
- data/lib/engineyard/{account → model}/api_struct.rb +8 -3
- data/lib/engineyard/{account → model}/app.rb +3 -3
- data/lib/engineyard/{account → model}/environment.rb +14 -7
- data/lib/engineyard/model/instance.rb +91 -0
- data/lib/engineyard/{account → model}/log.rb +1 -1
- data/lib/engineyard/repo.rb +3 -3
- data/spec/engineyard/{account → model}/api_struct_spec.rb +11 -7
- data/spec/engineyard/model/environment_spec.rb +45 -0
- data/spec/engineyard/model/instance_spec.rb +70 -0
- data/spec/engineyard/repo_spec.rb +17 -14
- data/spec/ey/deploy_spec.rb +95 -45
- data/spec/ey/list_environments_spec.rb +11 -1
- data/spec/ey/logs_spec.rb +18 -0
- data/spec/ey/rebuild_spec.rb +21 -4
- data/spec/ey/ssh_spec.rb +28 -3
- data/spec/spec_helper.rb +37 -12
- data/spec/support/fake_awsm.ru +139 -4
- data/spec/support/helpers.rb +19 -8
- metadata +31 -20
- data/lib/engineyard/account.rb +0 -63
- data/lib/engineyard/account/app_master.rb +0 -6
- data/lib/engineyard/account/instance.rb +0 -6
- data/lib/engineyard/action/deploy.rb +0 -138
- data/lib/engineyard/action/rebuild.rb +0 -31
- data/lib/engineyard/action/util.rb +0 -19
- data/spec/engineyard/account/environment_spec.rb +0 -20
- data/spec/engineyard/account_spec.rb +0 -18
data/lib/engineyard/repo.rb
CHANGED
@@ -15,9 +15,9 @@ module EY
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def urls
|
18
|
-
`git config -f #{@path}/.git/config --get-regexp 'remote.*.url'`.split(/\n/)
|
19
|
-
|
20
|
-
|
18
|
+
lines = `git config -f #{@path}/.git/config --get-regexp 'remote.*.url'`.split(/\n/)
|
19
|
+
raise NoRemotesError.new(@path) if lines.empty?
|
20
|
+
lines.map { |c| c.split.last }
|
21
21
|
end
|
22
22
|
|
23
23
|
end # Repo
|
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe EY::
|
4
|
-
class Foo < EY::
|
5
|
-
class FooWithAccount < EY::Account::ApiStruct.new(:fruit, :account); end
|
3
|
+
describe EY::Model::ApiStruct do
|
4
|
+
class Foo < EY::Model::ApiStruct.new(:fruit, :veggie); end
|
6
5
|
|
7
6
|
it "acts like a normal struct" do
|
8
7
|
f = Foo.new("banana")
|
@@ -28,10 +27,15 @@ describe EY::Account::ApiStruct do
|
|
28
27
|
f.should == [Foo.new("banana")]
|
29
28
|
end
|
30
29
|
|
31
|
-
it "handles
|
32
|
-
|
33
|
-
|
30
|
+
it "handles a common-arguments hash as the second argument" do
|
31
|
+
foos = Foo.from_array(
|
32
|
+
[{:fruit => "banana"}, {:fruit => 'apple'}],
|
33
|
+
:veggie => 'kale')
|
34
|
+
foos.should == [
|
35
|
+
Foo.new("banana", "kale"),
|
36
|
+
Foo.new("apple", "kale"),
|
37
|
+
]
|
34
38
|
end
|
35
39
|
end
|
36
40
|
|
37
|
-
end
|
41
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "EY::Model::Environment#rebuild" do
|
4
|
+
it_should_behave_like "it has an api"
|
5
|
+
|
6
|
+
it "hits the rebuild action in the API" do
|
7
|
+
env = EY::Model::Environment.from_hash({
|
8
|
+
"id" => 46534,
|
9
|
+
"api" => @api,
|
10
|
+
})
|
11
|
+
|
12
|
+
FakeWeb.register_uri(:put,
|
13
|
+
"https://cloud.engineyard.com/api/v2/environments/#{env.id}/rebuild",
|
14
|
+
:body => {}.to_json)
|
15
|
+
|
16
|
+
env.rebuild
|
17
|
+
|
18
|
+
FakeWeb.should have_requested(:put, "https://cloud.engineyard.com/api/v2/environments/#{env.id}/rebuild")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "EY::Model::Environment#instances" do
|
23
|
+
it_should_behave_like "it has an api"
|
24
|
+
|
25
|
+
it "returns instances" do
|
26
|
+
env = EY::Model::Environment.from_hash({
|
27
|
+
"id" => 10291,
|
28
|
+
"api" => @api,
|
29
|
+
})
|
30
|
+
|
31
|
+
instance_data = {
|
32
|
+
"id" => "1",
|
33
|
+
"role" => "app_master",
|
34
|
+
"amazon_id" => "i-likebeer",
|
35
|
+
"public_hostname" => "banana_master"
|
36
|
+
}
|
37
|
+
FakeWeb.register_uri(:get,
|
38
|
+
"https://cloud.engineyard.com/api/v2/environments/#{env.id}/instances",
|
39
|
+
:body => {"instances" => [instance_data]}.to_json)
|
40
|
+
|
41
|
+
|
42
|
+
env.should have(1).instances
|
43
|
+
env.instances.first.should == EY::Model::Instance.from_hash(instance_data.merge(:environment => env))
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "EY::Model::Instance's script for checking ey-deploy's version" do
|
4
|
+
|
5
|
+
def fake_out_no_ey_deploy
|
6
|
+
Gem.should_receive(:source_index).and_return(Gem::SourceIndex.new)
|
7
|
+
end
|
8
|
+
|
9
|
+
def fake_out_installed_ey_deploy_version(version)
|
10
|
+
net_sftp_gem = Gem::Specification.new do |s|
|
11
|
+
s.authors = ["Jamis Buck"]
|
12
|
+
s.autorequire = "net/sftp"
|
13
|
+
s.date = Time.utc(2008, 2, 24)
|
14
|
+
s.email = "jamis@jamisbuck.org"
|
15
|
+
s.files = ["doc/faq",
|
16
|
+
# snip
|
17
|
+
"test/protocol/tc_driver.rb"]
|
18
|
+
s.homepage = "http://net-ssh.rubyforge.org/sftp"
|
19
|
+
s.name = "net-sftp"
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.rubygems_version = "1.3.5"
|
22
|
+
s.specification_version = 2
|
23
|
+
s.summary = "Net::SFTP is a pure-Ruby implementation of the SFTP client protocol."
|
24
|
+
s.test_files = ["test/ALL-TESTS.rb"]
|
25
|
+
s.version = Gem::Version.new("1.1.1")
|
26
|
+
end
|
27
|
+
|
28
|
+
ey_deploy_gem = Gem::Specification.new do |s|
|
29
|
+
s.name = 'ey-deploy'
|
30
|
+
s.authors = ["EY Cloud Team"]
|
31
|
+
s.date = Time.utc(2010, 1, 2)
|
32
|
+
s.files = ['lib/engineyard/ey-deploy.rb'] # or something
|
33
|
+
s.specification_version = 2
|
34
|
+
s.version = Gem::Version.new(version)
|
35
|
+
end
|
36
|
+
|
37
|
+
fake_source_index = Gem::SourceIndex.new(
|
38
|
+
'net-sftp-1.1.1' => net_sftp_gem,
|
39
|
+
"ey-deploy-#{version}" => ey_deploy_gem
|
40
|
+
)
|
41
|
+
Gem.should_receive(:source_index).and_return(fake_source_index)
|
42
|
+
end
|
43
|
+
|
44
|
+
def script_exit_status
|
45
|
+
eval EY::Model::Instance::CHECK_SCRIPT
|
46
|
+
rescue SystemExit => e
|
47
|
+
return e.status
|
48
|
+
end
|
49
|
+
|
50
|
+
it "exits 104 if the ey-deploy gem is not installed" do
|
51
|
+
fake_out_no_ey_deploy
|
52
|
+
script_exit_status.should == 104
|
53
|
+
end
|
54
|
+
|
55
|
+
it "exits 70 if the installed ey-deploy is too old" do
|
56
|
+
fake_out_installed_ey_deploy_version('0.0.1')
|
57
|
+
script_exit_status.should == 70
|
58
|
+
end
|
59
|
+
|
60
|
+
it "exits 17 if the installed ey-deploy is too new" do
|
61
|
+
fake_out_installed_ey_deploy_version('1000.0.0')
|
62
|
+
script_exit_status.should == 17
|
63
|
+
end
|
64
|
+
|
65
|
+
it "exits 0 if the version number is correct" do
|
66
|
+
correct_version = EY::Model::Instance::EYSD_VERSION.gsub(/[^\d\.]/, '')
|
67
|
+
fake_out_installed_ey_deploy_version(correct_version)
|
68
|
+
script_exit_status.should == 0
|
69
|
+
end
|
70
|
+
end
|
@@ -26,26 +26,29 @@ describe EY::Repo do
|
|
26
26
|
it "returns the urls of the remotes" do
|
27
27
|
origin_url = "git://github.com/engineyard/engineyard.git"
|
28
28
|
other_url = "git@github.com:engineyard/engineyard.git"
|
29
|
-
set_url origin_url
|
30
|
-
set_url other_url,
|
29
|
+
set_url origin_url, "origin"
|
30
|
+
set_url other_url, "other"
|
31
31
|
@r.urls.should include(origin_url)
|
32
32
|
@r.urls.should include(other_url)
|
33
33
|
end
|
34
34
|
|
35
|
-
it "
|
36
|
-
|
37
|
-
@r.urls.should
|
35
|
+
it "raises EY::NoRemotesError if there is no origin remote" do
|
36
|
+
clear_urls
|
37
|
+
lambda { @r.urls }.should raise_error(EY::NoRemotesError)
|
38
38
|
end
|
39
39
|
|
40
|
-
def
|
41
|
-
@
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
40
|
+
def config_path
|
41
|
+
@path+"config"
|
42
|
+
end
|
43
|
+
|
44
|
+
# This has to all shell out because FakeFS is enabled
|
45
|
+
def set_url(url, remote)
|
46
|
+
system("mkdir -p #{@path} && cd #{@path} && git init -q")
|
47
|
+
system("git config -f #{config_path} remote.#{remote}.url #{url}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def clear_urls
|
51
|
+
system("rm -rf #{config_path}")
|
49
52
|
end
|
50
53
|
end # url
|
51
54
|
|
data/spec/ey/deploy_spec.rb
CHANGED
@@ -1,38 +1,24 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe "ey deploy" do
|
4
|
-
# like the "an integration test" setup, but without the ~/.eyrc file
|
5
|
-
# so we can test creating it
|
6
|
-
before(:all) do
|
7
|
-
FakeFS.deactivate!
|
8
|
-
ENV['EYRC'] = "/tmp/eyrc"
|
9
|
-
ENV['CLOUD_URL'] = EY.fake_awsm
|
10
|
-
FakeWeb.allow_net_connect = true
|
11
|
-
end
|
3
|
+
describe "ey deploy without an eyrc file" do
|
12
4
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
5
|
+
it_should_behave_like "an integration test without an eyrc file"
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
FileUtils.rm_rf(ENV['EYRC'])
|
9
|
+
api_scenario "one app, one environment"
|
17
10
|
end
|
18
11
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
12
|
+
it "prompts for authentication before continuing" do
|
13
|
+
ey("deploy", :hide_err => true) do |input|
|
14
|
+
input.puts("test@test.test")
|
15
|
+
input.puts("test")
|
23
16
|
end
|
24
17
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
@out.should include("We need to fetch your API token, please login")
|
32
|
-
@out.should include("Email:")
|
33
|
-
@out.should include("Password:")
|
34
|
-
@ssh_commands.should_not be_empty
|
35
|
-
end
|
18
|
+
@out.should include("We need to fetch your API token, please login")
|
19
|
+
@out.should include("Email:")
|
20
|
+
@out.should include("Password:")
|
21
|
+
@ssh_commands.should_not be_empty
|
36
22
|
end
|
37
23
|
end
|
38
24
|
|
@@ -42,25 +28,25 @@ describe "ey deploy" do
|
|
42
28
|
context "with invalid input" do
|
43
29
|
it "complains when there is no app" do
|
44
30
|
api_scenario "empty"
|
45
|
-
ey "deploy", :
|
31
|
+
ey "deploy", :expect_failure => true
|
46
32
|
@err.should include(%|no application configured|)
|
47
33
|
end
|
48
34
|
|
49
35
|
it "complains when you specify a nonexistent environment" do
|
50
36
|
api_scenario "one app, one environment"
|
51
|
-
ey "deploy typo-happens-here master", :
|
37
|
+
ey "deploy typo-happens-here master", :expect_failure => true
|
52
38
|
@err.should match(/no environment named 'typo-happens-here'/i)
|
53
39
|
end
|
54
40
|
|
55
41
|
it "complains when the specified environment does not contain the app" do
|
56
42
|
api_scenario "one app, one environment, not linked"
|
57
|
-
ey "deploy giblets master", :
|
43
|
+
ey "deploy giblets master", :expect_failure => true
|
58
44
|
@err.should match(/doesn't run this application/i)
|
59
45
|
end
|
60
46
|
|
61
47
|
it "complains when environment is ambiguous" do
|
62
48
|
api_scenario "one app, two environments"
|
63
|
-
ey "deploy", :
|
49
|
+
ey "deploy", :expect_failure => true
|
64
50
|
@err.should match(/was called incorrectly/i)
|
65
51
|
end
|
66
52
|
end
|
@@ -85,7 +71,7 @@ describe "ey deploy" do
|
|
85
71
|
it "defaults to 'rake db:migrate'" do
|
86
72
|
ey "deploy"
|
87
73
|
@ssh_commands.last.should =~ /eysd deploy/
|
88
|
-
@ssh_commands.last.should =~ /--migrate
|
74
|
+
@ssh_commands.last.should =~ /--migrate 'rake db:migrate'/
|
89
75
|
end
|
90
76
|
|
91
77
|
it "can be disabled with --no-migrate" do
|
@@ -98,12 +84,6 @@ describe "ey deploy" do
|
|
98
84
|
ey "deploy --no-migrate giblets master"
|
99
85
|
@ssh_commands.last.should_not =~ /--migrate/
|
100
86
|
end
|
101
|
-
|
102
|
-
it "can be disabled with --no-migrate" do
|
103
|
-
ey "deploy --no-migrate"
|
104
|
-
@ssh_commands.last.should =~ /eysd deploy/
|
105
|
-
@ssh_commands.last.should_not =~ /--migrate/
|
106
|
-
end
|
107
87
|
end
|
108
88
|
|
109
89
|
context "choosing something to deploy" do
|
@@ -127,6 +107,8 @@ describe "ey deploy" do
|
|
127
107
|
[
|
128
108
|
# initial repo setup
|
129
109
|
'git init >/dev/null 2>&1',
|
110
|
+
'git config user.email deploy@spec.test',
|
111
|
+
'git config user.name "Deploy Spec"',
|
130
112
|
'git remote add origin "user@git.host/path/to/repo.git"',
|
131
113
|
|
132
114
|
# we'll have one commit on master
|
@@ -171,6 +153,22 @@ describe "ey deploy" do
|
|
171
153
|
end
|
172
154
|
end
|
173
155
|
|
156
|
+
context "when there is extra configuration" do
|
157
|
+
before(:all) do
|
158
|
+
write_yaml({"environments" => {"giblets" => {"bert" => "ernie"}}},
|
159
|
+
File.join(@local_git_dir, "ey.yml"))
|
160
|
+
end
|
161
|
+
|
162
|
+
after(:all) do
|
163
|
+
File.unlink(File.join(@local_git_dir, "ey.yml"))
|
164
|
+
end
|
165
|
+
|
166
|
+
it "gets passed along to eysd" do
|
167
|
+
ey "deploy"
|
168
|
+
@ssh_commands.last.should =~ /--config '\{\"bert\":\"ernie\"\}'/
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
174
172
|
context "with a configured default branch" do
|
175
173
|
before(:all) do
|
176
174
|
write_yaml({"environments" => {"giblets" => {"branch" => "master"}}},
|
@@ -187,7 +185,7 @@ describe "ey deploy" do
|
|
187
185
|
end
|
188
186
|
|
189
187
|
it "complains about a non-default branch without --force" do
|
190
|
-
ey "deploy giblets current-branch", :
|
188
|
+
ey "deploy giblets current-branch", :expect_failure => true
|
191
189
|
@err.should =~ /deploy branch is set to "master"/
|
192
190
|
end
|
193
191
|
|
@@ -198,25 +196,77 @@ describe "ey deploy" do
|
|
198
196
|
end
|
199
197
|
end
|
200
198
|
|
199
|
+
context "specifying an environment" do
|
200
|
+
before(:all) do
|
201
|
+
api_scenario "one app, many similarly-named environments"
|
202
|
+
end
|
203
|
+
|
204
|
+
it "lets you choose by unambiguous substring" do
|
205
|
+
ey "deploy prod"
|
206
|
+
@out.should match(/Running deploy for 'railsapp_production'/)
|
207
|
+
end
|
208
|
+
|
209
|
+
it "lets you choose by complete name even if the complete name is ambiguous" do
|
210
|
+
ey "deploy railsapp_staging"
|
211
|
+
@out.should match(/Running deploy for 'railsapp_staging'/)
|
212
|
+
end
|
213
|
+
|
214
|
+
it "complains when given an ambiguous substring" do
|
215
|
+
# NB: there's railsapp_staging and railsapp_staging_2
|
216
|
+
ey "deploy staging", :hide_err => true, :expect_failure => true
|
217
|
+
@err.should match(/'staging' is ambiguous/)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
201
221
|
context "eysd install" do
|
202
222
|
before(:all) do
|
203
223
|
api_scenario "one app, one environment"
|
204
224
|
end
|
205
225
|
|
206
|
-
|
226
|
+
before(:each) do
|
227
|
+
ENV.delete "NO_SSH"
|
228
|
+
end
|
229
|
+
|
230
|
+
after(:each) do
|
207
231
|
ENV['NO_SSH'] = "true"
|
208
232
|
end
|
209
233
|
|
210
|
-
|
211
|
-
|
212
|
-
|
234
|
+
def exiting_ssh(exit_code)
|
235
|
+
"#!/usr/bin/env ruby\n exit!(#{exit_code}) if ARGV.to_s =~ /Base64.decode64/"
|
236
|
+
end
|
237
|
+
|
238
|
+
it "raises an error if SSH fails" do
|
239
|
+
ey "deploy", :prepend_to_path => {'ssh' => exiting_ssh(255)}, :expect_failure => true
|
240
|
+
@err.should =~ /SSH connection to \S+ failed/
|
241
|
+
end
|
213
242
|
|
214
|
-
|
243
|
+
it "installs ey-deploy if it's missing" do
|
244
|
+
ey "deploy", :prepend_to_path => {'ssh' => exiting_ssh(104)}
|
215
245
|
|
216
246
|
gem_install_command = @ssh_commands.find do |command|
|
217
247
|
command =~ /gem install ey-deploy/
|
218
248
|
end
|
219
249
|
gem_install_command.should =~ %r{/usr/local/ey_resin/ruby/bin/gem install}
|
220
250
|
end
|
251
|
+
|
252
|
+
it "upgrades ey-deploy if it's too old" do
|
253
|
+
ey "deploy", :prepend_to_path => {'ssh' => exiting_ssh(70)}
|
254
|
+
@ssh_commands.should have_command_like(/gem uninstall -a -x ey-deploy/)
|
255
|
+
@ssh_commands.should have_command_like(/gem install ey-deploy/)
|
256
|
+
end
|
257
|
+
|
258
|
+
it "raises an error if ey-deploy is too new" do
|
259
|
+
ey "deploy", :prepend_to_path => {'ssh' => exiting_ssh(17)}, :expect_failure => true
|
260
|
+
@ssh_commands.should_not have_command_like(/gem install ey-deploy/)
|
261
|
+
@ssh_commands.should_not have_command_like(/eysd deploy/)
|
262
|
+
@err.should match(/too new/i)
|
263
|
+
end
|
264
|
+
|
265
|
+
it "does not change ey-deploy if its version is satisfactory" do
|
266
|
+
ey "deploy", :prepend_to_path => {'ssh' => exiting_ssh(0)}
|
267
|
+
@ssh_commands.should_not have_command_like(/gem install ey-deploy/)
|
268
|
+
@ssh_commands.should_not have_command_like(/gem uninstall.* ey-deploy/)
|
269
|
+
@ssh_commands.should have_command_like(/eysd deploy/)
|
270
|
+
end
|
221
271
|
end
|
222
272
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "ey environments" do
|
4
|
+
|
4
5
|
it_should_behave_like "an integration test"
|
5
6
|
|
6
7
|
before(:all) do
|
@@ -10,7 +11,16 @@ describe "ey environments" do
|
|
10
11
|
it "lists the environments your app is in" do
|
11
12
|
ey "environments"
|
12
13
|
@out.should =~ /giblets/
|
13
|
-
@out.should =~ /
|
14
|
+
@out.should =~ /bakon/
|
15
|
+
end
|
16
|
+
|
17
|
+
it "reports failure to find a git repo when not in one" do
|
18
|
+
api_git_remote('dontcare')
|
19
|
+
Dir.chdir("/tmp") do
|
20
|
+
ey "environments", :expect_failure => true
|
21
|
+
@err.should =~ /fatal: No git remotes found in .*\/tmp/
|
22
|
+
@out.should_not =~ /no application configured/
|
23
|
+
end
|
14
24
|
end
|
15
25
|
|
16
26
|
end
|