engineyard 0.2.11 → 0.2.12
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/engineyard.rb +4 -3
- data/lib/engineyard/#repo.rb# +24 -0
- data/lib/engineyard/account.rb +32 -11
- data/lib/engineyard/account/api_struct.rb +25 -0
- data/lib/engineyard/account/app.rb +11 -10
- data/lib/engineyard/account/app_master.rb +1 -7
- data/lib/engineyard/account/environment.rb +31 -16
- data/lib/engineyard/account/instance.rb +6 -0
- data/lib/engineyard/account/log.rb +7 -16
- data/lib/engineyard/action/deploy.rb +138 -0
- data/lib/engineyard/action/list_environments.rb +22 -0
- data/lib/engineyard/action/rebuild.rb +31 -0
- data/lib/engineyard/action/show_logs.rb +26 -0
- data/lib/engineyard/action/ssh.rb +19 -0
- data/lib/engineyard/action/upload_recipes.rb +17 -0
- data/lib/engineyard/action/util.rb +47 -0
- data/lib/engineyard/cli.rb +24 -150
- data/lib/engineyard/cli/thor_fixes.rb +26 -0
- data/lib/engineyard/error.rb +48 -0
- data/lib/engineyard/repo.rb +1 -1
- data/lib/engineyard/ruby_ext.rb +9 -0
- data/spec/engineyard/account/api_struct_spec.rb +37 -0
- data/spec/engineyard/account/environment_spec.rb +20 -0
- data/spec/engineyard/account_spec.rb +18 -0
- data/spec/engineyard/cli_spec.rb +3 -3
- data/spec/ey/deploy_spec.rb +166 -28
- data/spec/ey/ey_spec.rb +3 -3
- data/spec/ey/list_environments_spec.rb +16 -0
- data/spec/ey/logs_spec.rb +2 -17
- data/spec/ey/rebuild_spec.rb +25 -0
- data/spec/ey/ssh_spec.rb +24 -0
- data/spec/ey/upload_recipes_spec.rb +21 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/support/fake_awsm.ru +25 -1
- data/spec/support/helpers.rb +72 -7
- metadata +44 -56
- data/lib/engineyard/cli/error.rb +0 -44
- data/spec/spec.opts +0 -2
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "EY::Account::Environment#rebuild" do
|
4
|
+
it_should_behave_like "it has an account"
|
5
|
+
|
6
|
+
it "hits the rebuild action in the API" do
|
7
|
+
env = EY::Account::Environment.from_hash({
|
8
|
+
"id" => 46534,
|
9
|
+
"account" => @account
|
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
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EY::Account do
|
4
|
+
it_should_behave_like "it has an account"
|
5
|
+
|
6
|
+
it "returns instances" do
|
7
|
+
@env = EY::Account::Environment.from_hash({
|
8
|
+
"id" => 1, "name" => "banana", "instances_count" => 3,
|
9
|
+
"ssh_username" => "monkey", "apps" => {}, "account" => @account
|
10
|
+
})
|
11
|
+
@instance_data = {"id" => "1", "role" => "app_master",
|
12
|
+
"amazon_id" => "amazon_1", "public_hostname" => "banana_master"}
|
13
|
+
FakeWeb.register_uri(:get, "https://cloud.engineyard.com/api/v2/environments/#{@env.id}/instances",
|
14
|
+
:body => {"instances" => [@instance_data]}.to_json)
|
15
|
+
|
16
|
+
@account.instances_for(@env).first.should == EY::Account::Instance.from_hash(@instance_data)
|
17
|
+
end
|
18
|
+
end
|
data/spec/engineyard/cli_spec.rb
CHANGED
@@ -12,9 +12,9 @@ describe EY::CLI do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
it "provides error classes" do
|
15
|
-
EY::
|
16
|
-
EY::
|
17
|
-
EY::
|
15
|
+
EY::EnvironmentError.should be
|
16
|
+
EY::BranchMismatch.should be
|
17
|
+
EY::DeployArgumentError.should be
|
18
18
|
end
|
19
19
|
|
20
20
|
end # EY::CLI
|
data/spec/ey/deploy_spec.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "ey deploy" do
|
4
|
+
# like the "an integration test" setup, but without the ~/.eyrc file
|
5
|
+
# so we can test creating it
|
4
6
|
before(:all) do
|
5
7
|
FakeFS.deactivate!
|
6
8
|
ENV['EYRC'] = "/tmp/eyrc"
|
@@ -17,9 +19,10 @@ describe "ey deploy" do
|
|
17
19
|
describe "without an eyrc file" do
|
18
20
|
before(:each) do
|
19
21
|
FileUtils.rm_rf(ENV['EYRC'])
|
22
|
+
api_scenario "one app, one environment"
|
20
23
|
end
|
21
24
|
|
22
|
-
it "prompts for authentication" do
|
25
|
+
it "prompts for authentication before continuing" do
|
23
26
|
ey("deploy", :hide_err => true) do |input|
|
24
27
|
input.puts("test@test.test")
|
25
28
|
input.puts("test")
|
@@ -28,57 +31,192 @@ describe "ey deploy" do
|
|
28
31
|
@out.should include("We need to fetch your API token, please login")
|
29
32
|
@out.should include("Email:")
|
30
33
|
@out.should include("Password:")
|
34
|
+
@ssh_commands.should_not be_empty
|
31
35
|
end
|
32
36
|
end
|
37
|
+
end
|
33
38
|
|
34
|
-
|
35
|
-
|
36
|
-
token = { ENV['CLOUD_URL'] => {
|
37
|
-
"api_token" => "f81a1706ddaeb148cfb6235ddecfc1cf"} }
|
38
|
-
File.open(ENV['EYRC'], "w"){|f| YAML.dump(token, f) }
|
39
|
-
end
|
39
|
+
describe "ey deploy" do
|
40
|
+
it_should_behave_like "an integration test"
|
40
41
|
|
42
|
+
context "with invalid input" do
|
41
43
|
it "complains when there is no app" do
|
42
44
|
api_scenario "empty"
|
43
|
-
ey "deploy", :hide_err => true
|
45
|
+
ey "deploy", :hide_err => true, :expect_failure => true
|
44
46
|
@err.should include(%|no application configured|)
|
45
47
|
end
|
46
48
|
|
47
|
-
it "complains when
|
48
|
-
api_scenario "one app, one environment
|
49
|
-
ey "deploy
|
50
|
-
@err.should match(/
|
49
|
+
it "complains when you specify a nonexistent environment" do
|
50
|
+
api_scenario "one app, one environment"
|
51
|
+
ey "deploy typo-happens-here master", :hide_err => true, :expect_failure => true
|
52
|
+
@err.should match(/no environment named 'typo-happens-here'/i)
|
51
53
|
end
|
52
54
|
|
53
|
-
it "
|
54
|
-
api_scenario "one app, one environment"
|
55
|
-
ey "deploy", :hide_err => true
|
56
|
-
@
|
57
|
-
@err.should be_empty
|
55
|
+
it "complains when the specified environment does not contain the app" do
|
56
|
+
api_scenario "one app, one environment, not linked"
|
57
|
+
ey "deploy giblets master", :hide_err => true, :expect_failure => true
|
58
|
+
@err.should match(/doesn't run this application/i)
|
58
59
|
end
|
59
60
|
|
60
61
|
it "complains when environment is ambiguous" do
|
61
62
|
api_scenario "one app, two environments"
|
62
|
-
ey "deploy", :hide_err => true
|
63
|
+
ey "deploy", :hide_err => true, :expect_failure => true
|
63
64
|
@err.should match(/was called incorrectly/i)
|
64
65
|
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it "runs when environment is known" do
|
69
|
+
api_scenario "one app, one environment"
|
70
|
+
ey "deploy", :hide_err => true
|
71
|
+
@out.should match(/running deploy/i)
|
72
|
+
@err.should be_empty
|
73
|
+
end
|
74
|
+
|
75
|
+
context "migration command" do
|
76
|
+
before(:each) do
|
77
|
+
api_scenario "one app, one environment"
|
78
|
+
end
|
79
|
+
|
80
|
+
it "finds eysd despite its being buried in the filesystem" do
|
81
|
+
ey "deploy"
|
82
|
+
@ssh_commands.last.should =~ %r{/usr/local/ey_resin/ruby/bin/eysd}
|
83
|
+
end
|
84
|
+
|
85
|
+
it "defaults to 'rake db:migrate'" do
|
86
|
+
ey "deploy"
|
87
|
+
@ssh_commands.last.should =~ /eysd deploy/
|
88
|
+
@ssh_commands.last.should =~ /--migrate='rake db:migrate'/
|
89
|
+
end
|
90
|
+
|
91
|
+
it "can be disabled with --no-migrate" do
|
92
|
+
ey "deploy --no-migrate"
|
93
|
+
@ssh_commands.last.should =~ /eysd deploy/
|
94
|
+
@ssh_commands.last.should_not =~ /--migrate/
|
95
|
+
end
|
96
|
+
|
97
|
+
it "can be disabled with --no-migrate in the middle of the command line" do
|
98
|
+
ey "deploy --no-migrate giblets master"
|
99
|
+
@ssh_commands.last.should_not =~ /--migrate/
|
100
|
+
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
|
+
end
|
108
|
+
|
109
|
+
context "choosing something to deploy" do
|
110
|
+
before(:all) do
|
111
|
+
api_scenario "one app, one environment"
|
112
|
+
api_git_remote("user@git.host/path/to/repo.git")
|
113
|
+
end
|
114
|
+
|
115
|
+
after(:all) do
|
116
|
+
api_git_remote(nil)
|
117
|
+
end
|
118
|
+
|
119
|
+
before(:all) do
|
120
|
+
@local_git_dir = File.join(
|
121
|
+
Dir.tmpdir,
|
122
|
+
"ey_test_git_#{Time.now.tv_sec}_#{Time.now.tv_usec}_#{$$}")
|
123
|
+
|
124
|
+
Dir.mkdir(@local_git_dir)
|
125
|
+
|
126
|
+
Dir.chdir(@local_git_dir) do
|
127
|
+
[
|
128
|
+
# initial repo setup
|
129
|
+
'git init >/dev/null 2>&1',
|
130
|
+
'git remote add origin "user@git.host/path/to/repo.git"',
|
131
|
+
|
132
|
+
# we'll have one commit on master
|
133
|
+
"echo 'source :gemcutter' > Gemfile",
|
134
|
+
"git add Gemfile",
|
135
|
+
"git commit -m 'initial commit' >/dev/null 2>&1",
|
136
|
+
|
137
|
+
# and a tag
|
138
|
+
"git tag -a -m 'version one' v1",
|
139
|
+
|
140
|
+
# and we need a non-master branch
|
141
|
+
"git checkout -b current-branch >/dev/null 2>&1",
|
142
|
+
].each do |cmd|
|
143
|
+
system("#{cmd}") or raise "#{cmd} failed"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
before(:each) do
|
149
|
+
@original_dir = Dir.getwd
|
150
|
+
Dir.chdir(@local_git_dir)
|
151
|
+
end
|
152
|
+
|
153
|
+
after(:each) do
|
154
|
+
Dir.chdir(@original_dir)
|
155
|
+
end
|
65
156
|
|
66
|
-
context "
|
67
|
-
|
68
|
-
|
157
|
+
context "without a configured default branch" do
|
158
|
+
it "defaults to the checked-out local branch" do
|
159
|
+
ey "deploy"
|
160
|
+
@ssh_commands.last.should =~ /--branch current-branch/
|
69
161
|
end
|
70
162
|
|
71
|
-
it "
|
163
|
+
it "deploys another branch if given" do
|
164
|
+
ey "deploy giblets master"
|
165
|
+
@ssh_commands.last.should =~ /--branch master/
|
166
|
+
end
|
167
|
+
|
168
|
+
it "deploys a tag if given" do
|
169
|
+
ey "deploy giblets v1"
|
170
|
+
@ssh_commands.last.should =~ /--branch v1/
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context "with a configured default branch" do
|
175
|
+
before(:all) do
|
176
|
+
write_yaml({"environments" => {"giblets" => {"branch" => "master"}}},
|
177
|
+
File.join(@local_git_dir, "ey.yml"))
|
178
|
+
end
|
179
|
+
|
180
|
+
after(:all) do
|
181
|
+
File.unlink(File.join(@local_git_dir, "ey.yml"))
|
182
|
+
end
|
183
|
+
|
184
|
+
it "deploys the default branch by default" do
|
72
185
|
ey "deploy"
|
73
|
-
@ssh_commands.
|
74
|
-
@ssh_commands.first.should =~ /--migrate='rake db:migrate'/
|
186
|
+
@ssh_commands.last.should =~ /--branch master/
|
75
187
|
end
|
76
188
|
|
77
|
-
it "
|
78
|
-
ey "deploy
|
79
|
-
@
|
80
|
-
|
189
|
+
it "complains about a non-default branch without --force" do
|
190
|
+
ey "deploy giblets current-branch", :hide_err => true, :expect_failure => true
|
191
|
+
@err.should =~ /deploy branch is set to "master"/
|
192
|
+
end
|
193
|
+
|
194
|
+
it "deploys a non-default branch with --force" do
|
195
|
+
ey "deploy giblets current-branch --force"
|
196
|
+
@ssh_commands.last.should =~ /--branch current-branch/
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context "eysd install" do
|
202
|
+
before(:all) do
|
203
|
+
api_scenario "one app, one environment"
|
204
|
+
end
|
205
|
+
|
206
|
+
after(:all) do
|
207
|
+
ENV['NO_SSH'] = "true"
|
208
|
+
end
|
209
|
+
|
210
|
+
it "installs eysd if 'eysd check' fails" do
|
211
|
+
ENV.delete('NO_SSH')
|
212
|
+
fake_ssh_no_eysd = "#!/usr/bin/env ruby\n exit!(127) if ARGV.last =~ /eysd check/"
|
213
|
+
|
214
|
+
ey "deploy", :prepend_to_path => {'ssh' => fake_ssh_no_eysd}
|
215
|
+
|
216
|
+
gem_install_command = @ssh_commands.find do |command|
|
217
|
+
command =~ /gem install ey-deploy/
|
81
218
|
end
|
219
|
+
gem_install_command.should =~ %r{/usr/local/ey_resin/ruby/bin/gem install}
|
82
220
|
end
|
83
221
|
end
|
84
222
|
end
|
data/spec/ey/ey_spec.rb
CHANGED
@@ -3,14 +3,14 @@ require 'spec_helper'
|
|
3
3
|
describe "ey" do
|
4
4
|
context "run without arguments" do
|
5
5
|
it "prints usage information" do
|
6
|
-
ey.should include
|
6
|
+
ey.should include("Tasks:")
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
10
|
context "run with an argument that is not a command" do
|
11
11
|
it "tells the user that is not a command" do
|
12
12
|
ey "foobarbaz", :hide_err => true
|
13
|
-
@err.should include
|
13
|
+
@err.should include("Could not find task")
|
14
14
|
end
|
15
15
|
end
|
16
|
-
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ey environments" do
|
4
|
+
it_should_behave_like "an integration test"
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
api_scenario "one app, two environments"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "lists the environments your app is in" do
|
11
|
+
ey "environments"
|
12
|
+
@out.should =~ /giblets/
|
13
|
+
@out.should =~ /ham/
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/spec/ey/logs_spec.rb
CHANGED
@@ -1,24 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe "ey logs" do
|
4
|
-
|
5
|
-
ENV['CLOUD_URL'] = EY.fake_awsm
|
6
|
-
FakeWeb.allow_net_connect = true
|
4
|
+
it_should_behave_like "an integration test"
|
7
5
|
|
8
|
-
|
9
|
-
ENV['EYRC'] = "/tmp/eyrc"
|
10
|
-
token = { ENV['CLOUD_URL'] => {
|
11
|
-
"api_token" => "f81a1706ddaeb148cfb6235ddecfc1cf"} }
|
12
|
-
File.open(ENV['EYRC'], "w"){|f| YAML.dump(token, f) }
|
13
|
-
end
|
14
|
-
|
15
|
-
after(:all) do
|
16
|
-
ENV['CLOUD_URL'] = nil
|
17
|
-
FakeFS.activate!
|
18
|
-
FakeWeb.allow_net_connect = false
|
19
|
-
end
|
20
|
-
|
21
|
-
it "runs when environment is known" do
|
6
|
+
it "prints logs returned by awsm" do
|
22
7
|
api_scenario "one app, one environment"
|
23
8
|
ey "logs giblets"
|
24
9
|
@out.should match(/MAIN LOG OUTPUT/)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ey rebuild" do
|
4
|
+
it_should_behave_like "an integration test"
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
api_scenario "one app, one environment"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "works when the environment name is valid" do
|
11
|
+
ey "rebuild giblets"
|
12
|
+
@out.should =~ /Rebuilding giblets/i
|
13
|
+
end
|
14
|
+
|
15
|
+
it "rebuilds the current environment by default" do
|
16
|
+
ey "rebuild"
|
17
|
+
@out.should =~ /Rebuilding giblets/i
|
18
|
+
end
|
19
|
+
|
20
|
+
it "fails when the environment name is bogus" do
|
21
|
+
ey "rebuild typo", :hide_err => true, :expect_failure => true
|
22
|
+
@err.should match(/No environment named 'typo'/)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
data/spec/ey/ssh_spec.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ey ssh" do
|
4
|
+
it_should_behave_like "an integration test"
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
api_scenario "one app, one environment"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "SSH-es into the right environment" do
|
11
|
+
print_my_args = "#!/bin/sh\necho ssh $*"
|
12
|
+
|
13
|
+
ey "ssh giblets", :prepend_to_path => {'ssh' => print_my_args}
|
14
|
+
@ssh_commands.should == ["ssh turkey@174.129.198.124"]
|
15
|
+
end
|
16
|
+
|
17
|
+
it "complains if you give it a bogus environment" do
|
18
|
+
print_my_args = "#!/bin/sh\necho ssh $*"
|
19
|
+
|
20
|
+
ey "ssh bogusenv", :prepend_to_path => {'ssh' => print_my_args}, :hide_err => true
|
21
|
+
@ssh_commands.should be_empty
|
22
|
+
@out.should =~ /could not find.*bogusenv/i
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "ey upload_recipes" do
|
4
|
+
it_should_behave_like "an integration test"
|
5
|
+
|
6
|
+
it "posts the recipes to the correct url" do
|
7
|
+
api_scenario "one app, one environment"
|
8
|
+
dir = Pathname.new("/tmp/#{$$}")
|
9
|
+
dir.mkdir
|
10
|
+
Dir.chdir(dir) do
|
11
|
+
dir.join("cookbooks").mkdir
|
12
|
+
File.open(dir.join("cookbooks/file"), "w"){|f| f << "boo" }
|
13
|
+
`git init`
|
14
|
+
`git add .`
|
15
|
+
`git commit -m "OMG"`
|
16
|
+
ey "upload_recipes giblets", :debug => true
|
17
|
+
end
|
18
|
+
|
19
|
+
@out.should =~ /recipes uploaded successfully/i
|
20
|
+
end
|
21
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
if self.class.const_defined?(:EY_ROOT)
|
2
|
+
raise "don't require the spec helper twice!"
|
3
|
+
end
|
4
|
+
|
1
5
|
EY_ROOT = File.expand_path("../..", __FILE__)
|
2
6
|
begin
|
3
7
|
require File.join(EY_ROOT, ".bundle/environment.rb")
|
@@ -8,6 +12,7 @@ end
|
|
8
12
|
|
9
13
|
# Bundled gems
|
10
14
|
require 'fakeweb'
|
15
|
+
require 'fakeweb_matcher'
|
11
16
|
require 'fakefs/safe'
|
12
17
|
|
13
18
|
# Engineyard gem
|
@@ -37,3 +42,30 @@ Spec::Runner.configure do |config|
|
|
37
42
|
EY.instance_eval{ @config = nil }
|
38
43
|
end
|
39
44
|
end
|
45
|
+
|
46
|
+
# Use this in conjunction with the 'ey' helper method
|
47
|
+
shared_examples_for "an integration test" do
|
48
|
+
before(:all) do
|
49
|
+
FakeFS.deactivate!
|
50
|
+
ENV['EYRC'] = "/tmp/eyrc"
|
51
|
+
ENV['CLOUD_URL'] = EY.fake_awsm
|
52
|
+
FakeWeb.allow_net_connect = true
|
53
|
+
|
54
|
+
token = { ENV['CLOUD_URL'] => {
|
55
|
+
"api_token" => "f81a1706ddaeb148cfb6235ddecfc1cf"} }
|
56
|
+
File.open(ENV['EYRC'], "w"){|f| YAML.dump(token, f) }
|
57
|
+
end
|
58
|
+
|
59
|
+
after(:all) do
|
60
|
+
ENV['CLOUD_URL'] = nil
|
61
|
+
FakeFS.activate!
|
62
|
+
FakeWeb.allow_net_connect = false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
shared_examples_for "it has an account" do
|
67
|
+
before(:all) do
|
68
|
+
write_yaml({"api_token" => "asdf"}, "~/.eyrc")
|
69
|
+
@account = EY::Account.new(EY::API.new)
|
70
|
+
end
|
71
|
+
end
|