engineyard 1.4.29 → 1.7.0.pre2

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