engineyard 1.4.29 → 1.7.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/README.rdoc +139 -4
  2. data/bin/ey +1 -7
  3. data/lib/engineyard.rb +1 -22
  4. data/lib/engineyard/cli.rb +192 -94
  5. data/lib/engineyard/cli/#recipes.rb# +32 -0
  6. data/lib/engineyard/cli/api.rb +42 -28
  7. data/lib/engineyard/cli/recipes.rb +13 -6
  8. data/lib/engineyard/cli/ui.rb +103 -42
  9. data/lib/engineyard/cli/web.rb +16 -10
  10. data/lib/engineyard/config.rb +92 -18
  11. data/lib/engineyard/deploy_config.rb +66 -0
  12. data/lib/engineyard/deploy_config/migrate.rb +125 -0
  13. data/lib/engineyard/deploy_config/ref.rb +56 -0
  14. data/lib/engineyard/error.rb +38 -78
  15. data/lib/engineyard/repo.rb +75 -27
  16. data/lib/engineyard/serverside_runner.rb +133 -0
  17. data/lib/engineyard/thor.rb +110 -18
  18. data/lib/engineyard/version.rb +1 -1
  19. data/spec/engineyard/cli/api_spec.rb +10 -16
  20. data/spec/engineyard/cli_spec.rb +0 -11
  21. data/spec/engineyard/config_spec.rb +1 -8
  22. data/spec/engineyard/deploy_config_spec.rb +203 -0
  23. data/spec/engineyard/eyrc_spec.rb +2 -0
  24. data/spec/engineyard/repo_spec.rb +57 -34
  25. data/spec/ey/deploy_spec.rb +102 -52
  26. data/spec/ey/list_environments_spec.rb +69 -14
  27. data/spec/ey/login_spec.rb +11 -7
  28. data/spec/ey/logout_spec.rb +4 -4
  29. data/spec/ey/logs_spec.rb +6 -6
  30. data/spec/ey/recipes/apply_spec.rb +1 -1
  31. data/spec/ey/recipes/download_spec.rb +1 -1
  32. data/spec/ey/recipes/upload_spec.rb +6 -6
  33. data/spec/ey/rollback_spec.rb +3 -3
  34. data/spec/ey/ssh_spec.rb +9 -9
  35. data/spec/ey/status_spec.rb +2 -2
  36. data/spec/ey/whoami_spec.rb +9 -8
  37. data/spec/spec_helper.rb +18 -15
  38. data/spec/support/{fake_awsm.rb → git_repos.rb} +0 -14
  39. data/spec/support/helpers.rb +84 -28
  40. data/spec/support/matchers.rb +0 -16
  41. data/spec/support/shared_behavior.rb +83 -103
  42. metadata +65 -51
  43. data/lib/engineyard/api.rb +0 -117
  44. data/lib/engineyard/collection.rb +0 -7
  45. data/lib/engineyard/collection/abstract.rb +0 -71
  46. data/lib/engineyard/collection/apps.rb +0 -8
  47. data/lib/engineyard/collection/environments.rb +0 -8
  48. data/lib/engineyard/model.rb +0 -12
  49. data/lib/engineyard/model/account.rb +0 -8
  50. data/lib/engineyard/model/api_struct.rb +0 -33
  51. data/lib/engineyard/model/app.rb +0 -32
  52. data/lib/engineyard/model/deployment.rb +0 -90
  53. data/lib/engineyard/model/environment.rb +0 -194
  54. data/lib/engineyard/model/instance.rb +0 -166
  55. data/lib/engineyard/model/log.rb +0 -9
  56. data/lib/engineyard/model/user.rb +0 -6
  57. data/lib/engineyard/resolver.rb +0 -134
  58. data/lib/engineyard/rest_client_ext.rb +0 -9
  59. data/lib/engineyard/ruby_ext.rb +0 -9
  60. data/spec/engineyard/api_spec.rb +0 -39
  61. data/spec/engineyard/collection/apps_spec.rb +0 -16
  62. data/spec/engineyard/collection/environments_spec.rb +0 -16
  63. data/spec/engineyard/model/api_struct_spec.rb +0 -41
  64. data/spec/engineyard/model/environment_spec.rb +0 -198
  65. data/spec/engineyard/model/instance_spec.rb +0 -27
  66. data/spec/engineyard/resolver_spec.rb +0 -112
  67. data/spec/support/fake_awsm.ru +0 -245
  68. data/spec/support/scenarios.rb +0 -417
@@ -1,9 +0,0 @@
1
- module EY
2
- module Model
3
- class Log < ApiStruct.new(:id, :role, :main, :custom)
4
- def instance_name
5
- "#{role} #{id}"
6
- end
7
- end
8
- end
9
- end
@@ -1,6 +0,0 @@
1
- module EY
2
- module Model
3
- class User < ApiStruct.new(:id, :name, :email)
4
- end
5
- end
6
- end
@@ -1,134 +0,0 @@
1
- module EY
2
- class Resolver
3
- attr_reader :api
4
-
5
- def initialize(api)
6
- @api = api
7
- end
8
-
9
- def environment(options)
10
- raise ArgumentError if options[:app_name]
11
- candidates, account_candidates, app_candidates, environment_candidates = filter_candidates(options)
12
-
13
- environments = candidates.map{ |c| [c[:account_name], c[:environment_name]] }.uniq.map do |account_name, environment_name|
14
- api.environments.named(environment_name, account_name)
15
- end
16
-
17
- if environments.empty?
18
- if options[:environment_name]
19
- raise EY::NoEnvironmentError.new(options[:environment_name])
20
- else
21
- raise EY::NoAppError.new(options[:repo])
22
- end
23
- elsif environments.size > 1
24
- if options[:environment_name]
25
- message = "Multiple environments possible, please be more specific:\n\n"
26
- candidates.map{|e| [e[:account_name], e[:environment_name]]}.uniq.each do |account_name, environment_name|
27
- message << "\t#{environment_name.ljust(25)} # ey <command> --environment='#{environment_name}' --account='#{account_name}'\n"
28
- end
29
- raise MultipleMatchesError.new(message)
30
- else
31
- raise EY::AmbiguousEnvironmentGitUriError.new(environments)
32
- end
33
- end
34
- environments.first
35
- end
36
-
37
- def app_and_environment(options)
38
- candidates, account_candidates, app_candidates, environment_candidates = filter_candidates(options)
39
-
40
- if candidates.empty?
41
- if account_candidates.empty? && options[:account_name]
42
- raise NoMatchesError.new("There were no accounts that matched #{options[:account_name]}")
43
- elsif app_candidates.empty?
44
- if options[:app_name]
45
- raise InvalidAppError.new(options[:app_name])
46
- else
47
- raise NoAppError.new(options[:repo])
48
- end
49
- elsif environment_candidates.empty?
50
- raise NoEnvironmentError.new(options[:environment_name])
51
- else
52
- message = "The matched apps & environments do not correspond with each other.\n"
53
- message << "Applications:\n"
54
- app_candidates.map{|ad| [ad[:account_name], ad[:app_name]]}.uniq.each do |account_name, app_name|
55
- app = api.apps.named(app_name, account_name)
56
- message << "\t#{app.name}\n"
57
- app.environments.each do |env|
58
- message << "\t\t#{env.name} # ey <command> -e #{env.name} -a #{app.name}\n"
59
- end
60
- end
61
- end
62
- raise NoMatchesError.new(message)
63
- elsif candidates.size > 1
64
- message = "Multiple app deployments possible, please be more specific:\n\n"
65
- candidates.map{|c| [c[:account_name], c[:app_name]]}.uniq.each do |account_name, app_name|
66
- message << "#{app_name}\n"
67
- candidates.select {|c| c[:app_name] == app_name && c[:account_name] == account_name}.map{|c| c[:environment_name]}.uniq.each do |env_name|
68
- message << "\t#{env_name.ljust(25)} # ey <command> --environment='#{env_name}' --app='#{app_name}' --account='#{account_name}'\n"
69
- end
70
- end
71
- raise MultipleMatchesError.new(message)
72
- end
73
- result = candidates.first
74
- [api.apps.named(result[:app_name], result[:account_name]), api.environments.named(result[:environment_name], result[:account_name])]
75
- end
76
-
77
- private
78
-
79
- def app_deployments
80
- @app_deployments ||= api.apps.map do |app|
81
- app.environments.map do |environment|
82
- {
83
- :app_name => app.name.downcase,
84
- :repository_uri => app.repository_uri,
85
- :environment_name => environment.name.downcase,
86
- :account_name => app.account.name.downcase,
87
- }
88
- end
89
- end.flatten
90
- end
91
-
92
- def filter_candidates(options)
93
- raise ArgumentError if options.empty?
94
-
95
- candidates = app_deployments
96
-
97
- account_candidates = filter_candidates_by(:account_name, options, candidates)
98
-
99
- app_candidates = if options[:app_name]
100
- filter_candidates_by(:app_name, options, candidates)
101
- elsif options[:repo]
102
- filter_by_repo(candidates, options[:repo])
103
- else
104
- candidates
105
- end
106
-
107
- environment_candidates = filter_candidates_by(:environment_name, options, candidates)
108
- candidates = app_candidates & environment_candidates & account_candidates
109
- [candidates, account_candidates, app_candidates, environment_candidates]
110
- end
111
-
112
- def filter_by_repo(candidates, repo)
113
- results = candidates.select do |candidate|
114
- repo.has_remote?(candidate[:repository_uri])
115
- end
116
-
117
- if results.empty?
118
- candidates
119
- else
120
- results
121
- end
122
- end
123
-
124
- def filter_candidates_by(type, options, candidates)
125
- if options[type] && candidates.any?{|c| c[type] == options[type].downcase }
126
- candidates.select {|c| c[type] == options[type].downcase }
127
- elsif options[type]
128
- candidates.select {|c| c[type][options[type].downcase] }
129
- else
130
- candidates
131
- end
132
- end
133
- end
134
- end
@@ -1,9 +0,0 @@
1
- module RestClient
2
- module AbstractResponse
3
- private
4
-
5
- def parse_cookie cookie_content
6
- {}
7
- end
8
- end
9
- end
@@ -1,9 +0,0 @@
1
- class Object
2
- unless respond_to?(:tap)
3
- # Ruby 1.9 has it, 1.8 doesn't
4
- def tap
5
- yield self
6
- self
7
- end
8
- end
9
- end
@@ -1,39 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe EY::API do
4
- it "gets the api token from ~/.eyrc if possible" do
5
- write_eyrc({"api_token" => "asdf"})
6
- EY::API.new.should == EY::API.new("asdf")
7
- end
8
-
9
- context "fetching the token from EY cloud" do
10
- before(:each) do
11
- FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/authenticate", :body => %|{"api_token": "asdf"}|, :content_type => 'application/json')
12
- @token = EY::API.fetch_token("a@b.com", "foo")
13
- end
14
-
15
- it "returns an EY::API" do
16
- @token.should == "asdf"
17
- end
18
-
19
- it "puts the api token into .eyrc" do
20
- read_eyrc["api_token"].should == "asdf"
21
- end
22
- end
23
-
24
- it "raises InvalidCredentials when the credentials are invalid" do
25
- FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/authenticate", :status => 401, :content_type => 'application/json')
26
-
27
- lambda {
28
- EY::API.fetch_token("a@b.com", "foo")
29
- }.should raise_error(EY::Error)
30
- end
31
-
32
- it "raises RequestFailed with a friendly error when cloud is under maintenance" do
33
- FakeWeb.register_uri(:post, "https://cloud.engineyard.com/api/v2/authenticate", :status => 502, :content_type => 'text/html')
34
-
35
- lambda {
36
- EY::API.fetch_token("a@b.com", "foo")
37
- }.should raise_error(EY::API::RequestFailed, /API is temporarily unavailable/)
38
- end
39
- end
@@ -1,16 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe EY::Collection::Apps do
4
- before do
5
- @collection_class = EY::Collection::Apps
6
- @collection = @collection_class.new([
7
- EY::Model::App.from_hash("id" => 1234, "name" => "app_production"),
8
- EY::Model::App.from_hash("id" => 4321, "name" => "app_staging"),
9
- EY::Model::App.from_hash("id" => 8765, "name" => "bigapp_staging"),
10
- EY::Model::App.from_hash("id" => 4532, "name" => "app_duplicate"),
11
- EY::Model::App.from_hash("id" => 4533, "name" => "app_duplicate"),
12
- ])
13
- end
14
-
15
- include_examples "model collections"
16
- end
@@ -1,16 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe EY::Collection::Environments do
4
- before do
5
- @collection_class = EY::Collection::Environments
6
- @collection = @collection_class.new([
7
- EY::Model::Environment.from_hash("id" => 1234, "name" => "app_production"),
8
- EY::Model::Environment.from_hash("id" => 4321, "name" => "app_staging"),
9
- EY::Model::Environment.from_hash("id" => 8765, "name" => "bigapp_staging"),
10
- EY::Model::Environment.from_hash("id" => 4532, "name" => "app_duplicate"),
11
- EY::Model::Environment.from_hash("id" => 4533, "name" => "app_duplicate"),
12
- ])
13
- end
14
-
15
- include_examples "model collections"
16
- end
@@ -1,41 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe EY::Model::ApiStruct do
4
- class Foo < EY::Model::ApiStruct.new(:fruit, :veggie); end
5
-
6
- it "acts like a normal struct" do
7
- f = Foo.new("banana")
8
-
9
- f.fruit.should == "banana"
10
- end
11
-
12
- describe "from_hash initializer" do
13
- it "assigns values from string keys" do
14
- f = Foo.from_hash("fruit" => "banana")
15
- f.should == Foo.new("banana")
16
- end
17
-
18
- it "assigns values from symbol keys" do
19
- f = Foo.from_hash(:fruit => "banana")
20
- f.should == Foo.new("banana")
21
- end
22
- end
23
-
24
- describe "from_array initializer" do
25
- it "provides a from_array initializer" do
26
- f = Foo.from_array([:fruit => "banana"])
27
- f.should == [Foo.new("banana")]
28
- end
29
-
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
- ]
38
- end
39
- end
40
-
41
- end
@@ -1,198 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "EY::Model::Environment#rebuild" do
4
- it "hits the rebuild action in the API" do
5
- env = EY::Model::Environment.from_hash({
6
- "id" => 46534,
7
- "api" => ey_api,
8
- })
9
-
10
- FakeWeb.register_uri(
11
- :put,
12
- "https://cloud.engineyard.com/api/v2/environments/#{env.id}/update_instances",
13
- :body => ''
14
- )
15
-
16
- env.rebuild
17
-
18
- FakeWeb.should have_requested(:put, "https://cloud.engineyard.com/api/v2/environments/#{env.id}/update_instances")
19
- end
20
- end
21
-
22
- describe "EY::Model::Environment#run_custom_recipes" do
23
- it "hits the rebuild action in the API" do
24
- env = EY::Model::Environment.from_hash({
25
- "id" => 46534,
26
- "api" => ey_api,
27
- })
28
-
29
- FakeWeb.register_uri(
30
- :put,
31
- "https://cloud.engineyard.com/api/v2/environments/#{env.id}/run_custom_recipes",
32
- :body => '',
33
- :content_type => 'application/json'
34
- )
35
-
36
- env.run_custom_recipes
37
-
38
- FakeWeb.should have_requested(:put, "https://cloud.engineyard.com/api/v2/environments/#{env.id}/run_custom_recipes")
39
- end
40
- end
41
-
42
- describe "EY::Model::Environment.from_array" do
43
- it "returns a smart collection, not just a dumb array" do
44
- api_data = [
45
- {"id" => 32340, "name" => 'iceberg'},
46
- {"id" => 9433, "name" => 'zoidberg'},
47
- ]
48
-
49
- collection = EY::Model::Environment.from_array(api_data)
50
- collection.should respond_to(:each)
51
- collection.should respond_to(:match_one)
52
- end
53
- end
54
-
55
- describe "EY::Model::Environment#instances" do
56
- it "returns instances" do
57
- instance_data = {
58
- "id" => "1",
59
- "role" => "app_master",
60
- "amazon_id" => "i-likebeer",
61
- "public_hostname" => "banana_master"
62
- }
63
-
64
- env = EY::Model::Environment.from_hash({
65
- "id" => 10291,
66
- "api" => ey_api,
67
- "instances" => [instance_data],
68
- })
69
-
70
- FakeWeb.register_uri(:get,
71
- "https://cloud.engineyard.com/api/v2/environments/#{env.id}/instances",
72
- :body => {"instances" => [instance_data]}.to_json,
73
- :content_type => 'application/json'
74
- )
75
-
76
- env.should have(1).instances
77
- env.instances.first.should == EY::Model::Instance.from_hash(instance_data.merge(:environment => env))
78
- end
79
- end
80
-
81
- describe "EY::Model::Environment#app_master!" do
82
- def make_env_with_master(app_master)
83
- if app_master
84
- app_master = {
85
- "id" => 44206,
86
- "role" => "solo",
87
- }.merge(app_master)
88
- end
89
-
90
- EY::Model::Environment.from_hash({
91
- "id" => 11830,
92
- "name" => "guinea-pigs-are-delicious",
93
- "app_master" => app_master,
94
- "instances" => [app_master].compact,
95
- })
96
- end
97
-
98
-
99
- it "returns the app master if it's present and running" do
100
- env = make_env_with_master("status" => "running")
101
- env.app_master!.should_not be_nil
102
- env.app_master!.id.should == 44206
103
- end
104
-
105
- it "raises an error if the app master is in a non-running state" do
106
- env = make_env_with_master("status" => "error")
107
- lambda {
108
- env.app_master!
109
- }.should raise_error(EY::BadAppMasterStatusError)
110
- end
111
-
112
- it "returns the app master if told to ignore the app master being in a non-running state" do
113
- env = make_env_with_master("status" => "error")
114
- env.ignore_bad_master = true
115
- env.app_master!.should_not be_nil
116
- env.app_master!.id.should == 44206
117
- end
118
-
119
- it "raises an error if the app master is absent" do
120
- env = make_env_with_master(nil)
121
- lambda {
122
- env.app_master!
123
- }.should raise_error(EY::NoAppMasterError)
124
- end
125
- end
126
-
127
- describe "EY::Model::Environment#shorten_name_for(app)" do
128
- def short(environment_name, app_name)
129
- env = EY::Model::Environment.from_hash({:name => environment_name})
130
- app = EY::Model::App.from_hash({:name => app_name})
131
- env.shorten_name_for(app)
132
- end
133
-
134
- it "turns myapp+myapp_production to production" do
135
- short('myapp_production', 'myapp').should == 'production'
136
- end
137
-
138
- it "turns product+production to product (leaves it alone)" do
139
- short('production', 'product').should == 'production'
140
- end
141
-
142
- it "leaves the environment name alone when the app name appears in the middle" do
143
- short('hattery', 'ate').should == 'hattery'
144
- end
145
-
146
- it "does not produce an empty string when the names are the same" do
147
- short('dev', 'dev').should == 'dev'
148
- end
149
- end
150
-
151
- describe "EY::Model::Environment#migration_command" do
152
- before do
153
- @app = EY::Model::App.from_hash({:name => 'fake'})
154
- @migrate = EY::Model::Environment.from_hash({
155
- "id" => 10291,
156
- "api" => ey_api,
157
- 'name' => 'migrate',
158
- 'deployment_configurations' => {'fake' => {'migrate' => {'command' => 'fake db:migrate', 'perform' => true}}}
159
- })
160
-
161
- @no_migrate = EY::Model::Environment.from_hash({
162
- "id" => 10291,
163
- "api" => ey_api,
164
- 'name' => 'no_migrate',
165
- 'deployment_configurations' => {'fake' => {'migrate' => {'command' => 'fake db:migrate', 'perform' => false}}}
166
- })
167
- end
168
-
169
- it "returns the migration command for the environment when the perform flag is true" do
170
- @migrate.migration_command(@app, {}).should == 'fake db:migrate'
171
- end
172
-
173
- it "returns nil when the perform flag in the environment is false" do
174
- @no_migrate.migration_command(@app, {}).should == nil
175
- end
176
-
177
- context "with the migrate deploy option" do
178
- it "returns the default migration command when is true" do
179
- @migrate.migration_command(@app, {'migrate' => true}).should == 'rake db:migrate'
180
- end
181
-
182
- it "return the custom migration command when is a string" do
183
- @migrate.migration_command(@app, {'migrate' => 'foo migrate'}).should == 'foo migrate'
184
- end
185
- end
186
-
187
- context "with the migrate option in the global configuration" do
188
- it "return the default migration command when the option is true" do
189
- EY.config.environments['migrate'] = {'migrate' => true, 'migration_command' => 'bar migrate'}
190
- @migrate.migration_command(@app, {}).should == 'bar migrate'
191
- end
192
-
193
- it "return the custom migration command when the option is a string" do
194
- EY.config.environments['migrate'] = {'migration_command' => 'bar migrate'}
195
- @migrate.migration_command(@app, {}).should == 'bar migrate'
196
- end
197
- end
198
- end