engineyard 0.3.1 → 0.3.2
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.
- 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
|