engineyard 0.2.11 → 0.2.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/lib/engineyard.rb +4 -3
  2. data/lib/engineyard/#repo.rb# +24 -0
  3. data/lib/engineyard/account.rb +32 -11
  4. data/lib/engineyard/account/api_struct.rb +25 -0
  5. data/lib/engineyard/account/app.rb +11 -10
  6. data/lib/engineyard/account/app_master.rb +1 -7
  7. data/lib/engineyard/account/environment.rb +31 -16
  8. data/lib/engineyard/account/instance.rb +6 -0
  9. data/lib/engineyard/account/log.rb +7 -16
  10. data/lib/engineyard/action/deploy.rb +138 -0
  11. data/lib/engineyard/action/list_environments.rb +22 -0
  12. data/lib/engineyard/action/rebuild.rb +31 -0
  13. data/lib/engineyard/action/show_logs.rb +26 -0
  14. data/lib/engineyard/action/ssh.rb +19 -0
  15. data/lib/engineyard/action/upload_recipes.rb +17 -0
  16. data/lib/engineyard/action/util.rb +47 -0
  17. data/lib/engineyard/cli.rb +24 -150
  18. data/lib/engineyard/cli/thor_fixes.rb +26 -0
  19. data/lib/engineyard/error.rb +48 -0
  20. data/lib/engineyard/repo.rb +1 -1
  21. data/lib/engineyard/ruby_ext.rb +9 -0
  22. data/spec/engineyard/account/api_struct_spec.rb +37 -0
  23. data/spec/engineyard/account/environment_spec.rb +20 -0
  24. data/spec/engineyard/account_spec.rb +18 -0
  25. data/spec/engineyard/cli_spec.rb +3 -3
  26. data/spec/ey/deploy_spec.rb +166 -28
  27. data/spec/ey/ey_spec.rb +3 -3
  28. data/spec/ey/list_environments_spec.rb +16 -0
  29. data/spec/ey/logs_spec.rb +2 -17
  30. data/spec/ey/rebuild_spec.rb +25 -0
  31. data/spec/ey/ssh_spec.rb +24 -0
  32. data/spec/ey/upload_recipes_spec.rb +21 -0
  33. data/spec/spec_helper.rb +32 -0
  34. data/spec/support/fake_awsm.ru +25 -1
  35. data/spec/support/helpers.rb +72 -7
  36. metadata +44 -56
  37. data/lib/engineyard/cli/error.rb +0 -44
  38. 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
@@ -12,9 +12,9 @@ describe EY::CLI do
12
12
  end
13
13
 
14
14
  it "provides error classes" do
15
- EY::CLI::EnvironmentError.should be
16
- EY::CLI::BranchMismatch.should be
17
- EY::CLI::DeployArgumentError.should be
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
@@ -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
- describe "with an eyrc file" do
35
- before(:each) do
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 there is no environment for the app" do
48
- api_scenario "one app, one environment, not linked"
49
- ey "deploy giblets master", :hide_err => true
50
- @err.should match(/doesn't run this application/i)
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 "runs when environment is known" do
54
- api_scenario "one app, one environment"
55
- ey "deploy", :hide_err => true
56
- @out.should match(/running deploy/i)
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 "migration command" do
67
- before(:each) do
68
- api_scenario "one app, one environment"
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 "defaults to 'rake db:migrate'" do
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.size.should == 1
74
- @ssh_commands.first.should =~ /--migrate='rake db:migrate'/
186
+ @ssh_commands.last.should =~ /--branch master/
75
187
  end
76
188
 
77
- it "can be disabled with --no-migrate" do
78
- ey "deploy --no-migrate"
79
- @ssh_commands.size.should == 1
80
- @ssh_commands.first.should_not =~ /--migrate/
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 "Tasks:"
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 "Could not find task"
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
- before(:all) do
5
- ENV['CLOUD_URL'] = EY.fake_awsm
6
- FakeWeb.allow_net_connect = true
4
+ it_should_behave_like "an integration test"
7
5
 
8
- FakeFS.deactivate!
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
+
@@ -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