engineyard 0.2.11 → 0.2.12

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.
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