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
data/README.rdoc CHANGED
@@ -20,10 +20,15 @@ The ey.yml file allows options to be saved for each environment to which an appl
20
20
  ---
21
21
  environments:
22
22
  env_production:
23
- migrate: false
24
- migration_command: rake fancy:migrate
25
- branch: deploy
26
- default: true # make this environment default
23
+ migrate: false # run migration command on every deploy
24
+ migration_command: rake fancy:migrate # default migration command
25
+ branch: deploy # default branch to deploy
26
+ default: true # make this environment default
27
+ bundle_without: test development mygroup # exclude groups on bundle install
28
+ copy_exclude: # don't rsync the following dirs
29
+ - .git
30
+ verbose: true # always run verbose deploy (unless overriden on command line)
31
+
27
32
 
28
33
  This ey.yml file will turn off default migrations, set the default command to "rake fancy:migrate" and set the default deploy branch to "deploy".
29
34
 
@@ -208,3 +213,133 @@ Command:
208
213
 
209
214
  Description:
210
215
  Remove the current API key from ~/.eyrc or env variable $EYRC
216
+
217
+
218
+ == API Client
219
+
220
+ This library also contains a Ruby ey_api library to the Engine Yard Cloud API.
221
+
222
+ Setup:
223
+
224
+ token = EY::CloudClient.authenticate("your@email.com", "password")
225
+ ey_api = EY::CloudClient.new(token)
226
+
227
+ Current User:
228
+
229
+ user = ey_api.current_user
230
+ user.class # => EY::CloudClient::User
231
+ user.name # => "Your Name"
232
+ user.email # => "your@email.com"
233
+
234
+ Apps:
235
+
236
+ apps = ey_api.apps # loads all your app data at once; caches result
237
+
238
+ app = apps.find {|app| app.name == 'myapp'}
239
+ app.class # => EY::CloudClient::App
240
+ app.name # => 'myapp'
241
+ app.id # => 123
242
+ app.repository_uri # => git@github.com:myaccount/myapp.git
243
+
244
+ app.account.class # => EY::CloudClient::Account
245
+
246
+ app.app_environments.first.class # => EY::CloudClient::AppEnvironment
247
+ app.app_environments.map {|e| e.environment.name} # => ['myapp_production', 'myapp_staging']
248
+
249
+ Create a new application (to be booted within Environments):
250
+
251
+ account = EY::CloudClient::Account.new(ey_api, {:id => 4212, :name => 'drnic'})
252
+ app = EY::CloudClient::App.create(ey_api,
253
+ "account" => account
254
+ "name" => "myapp",
255
+ "repository_uri" => "git@github.com:mycompany/myapp.git",
256
+ "app_type_id" => "rails3",
257
+ })
258
+
259
+ Valid `app_type_id` are: `rack, rails2, rails3, rails4, sinatra, nodejs`. For some your account may require an early access feature to be enabled.
260
+
261
+ Accounts:
262
+
263
+ account = app.account
264
+ account.class # => EY::CloudClient::Account
265
+ account.id # => 1234
266
+ account.name # => 'myaccount'
267
+
268
+ Keypairs:
269
+
270
+ Upload your SSH public keys before you create Environments.
271
+
272
+ keypair = EY::CloudClient::Keypair.create(ey_api, {
273
+ "name" => 'laptop',
274
+ "public_key" => "ssh-rsa OTHERKEYPAIR"
275
+ })
276
+
277
+ Environments:
278
+
279
+ envs = ey_api.environments # loads all your environment data at once; caches result
280
+
281
+ env = envs.find {|e| e.name == 'myapp_production'}
282
+ env.class # => EY::CloudClient::Environment
283
+ env.name # => 'myapp_production'
284
+ env.id # => 2345
285
+ env.framework_env # => "production"
286
+ env.app_server_stack_name # => "nginx_thin"
287
+ env.deployment_configurations # => {"myapp"=>{"name"=>"myapp", "uri"=>nil, "migrate"=>{"command"=>"rake db:migrate", "perform"=>false}, "repository_uri"=>"git@github.com:myaccount/myapp.git", "id"=>123, "domain_name"=>"_"}}
288
+ env.load_balancer_ip_address # => "1.2.3.4"
289
+
290
+ # if environment isn't booted
291
+ env.instances_count # => 0
292
+ env.app_master # => nil
293
+ env.instances.count # => []
294
+
295
+ # if environment is booted
296
+ env.instances_count # => 1
297
+ env.app_master.class # => EY::CloudClient::Instance
298
+ env.instances.first.class # => EY::CloudClient::Instance
299
+
300
+ Create a new environment (for a given App):
301
+
302
+ app = EY::CloudClient::App.new(ey_api, {:id => 4212, :name => 'drnic'})
303
+ env = EY::CloudClient::Environment.create(ey_api,
304
+ "app" => app,
305
+ "name" => 'myapp_production',
306
+ "app_server_stack_name" => 'nginx_thin', # default: nginx_passenger3
307
+ "region" => 'us-west-1', # default: us-east-1
308
+ "framework_env" => 'staging' # default: production
309
+ })
310
+
311
+
312
+ Valid `app_server_stack_name` values: `nginx_unicorn, nginx_passenger3, nginx_nodejs, nginx_thin, nginx_puma`. For some your account may require an early access feature to be enabled.
313
+
314
+ Instances:
315
+
316
+ instance = env.instances.first
317
+ instance.class # => EY::CloudClient::Instance
318
+ instance.id # => 12345
319
+ instance.role # => "solo"
320
+ instance.status # => "running"
321
+ instance.amazon_id # => "i-abcdefg"
322
+ instance.hostname # => "ec2-1-2-3-4.compute-1.amazonaws.com"
323
+ instance.public_hostname # => "ec2-1-2-3-4.compute-1.amazonaws.com" # alias of hostname
324
+
325
+ Debugging:
326
+
327
+ Setup for debugging:
328
+
329
+ ENV['DEBUG']='1'
330
+ require 'engineyard/cli'
331
+ EY.ui = EY::CLI::UI.new
332
+
333
+ The API commands will print internal information:
334
+
335
+ app = EY::CloudClient::App.create(ey_api, account, 'myapp2', 'git@github.com:myaccount/myapp2.git', 'rails3')
336
+ Token YOURTOKEN
337
+ Request POST https://cloud.engineyard.com/api/v2/accounts/1234/apps
338
+ Params {"app"=>{"name"=>"myapp2", "app_type_id"=>"rails3", "repository_uri"=>"git@github.com:myaccount/myapp2.git"}}
339
+ Response
340
+ {"app"=>
341
+ {"environments"=>[],
342
+ "name"=>"myapp2",
343
+ "repository_uri"=>"git@github.com:myaccount/myapp2.git",
344
+ "account"=>{"name"=>"myaccount", "id"=>1234},
345
+ "id"=>12345}}
data/bin/ey CHANGED
@@ -4,12 +4,6 @@ require 'engineyard/cli'
4
4
 
5
5
  begin
6
6
  EY::CLI.start
7
- rescue EY::Error => e
8
- EY.ui.print_exception(e)
9
- exit(1)
10
- rescue Interrupt => e
11
- puts
12
- EY.ui.print_exception(e)
13
- EY.ui.say("Quitting...")
7
+ rescue
14
8
  exit(1)
15
9
  end
data/lib/engineyard.rb CHANGED
@@ -1,32 +1,11 @@
1
1
  thor_lib = File.expand_path(File.join(File.dirname(__FILE__), 'vendor', 'thor', 'lib'))
2
2
  $:.unshift thor_lib
3
+ require 'engineyard-cloud-client'
3
4
 
4
5
  module EY
5
- require 'engineyard/ruby_ext'
6
6
  require 'engineyard/version'
7
7
  require 'engineyard/error'
8
8
  require 'engineyard/config'
9
9
 
10
- autoload :API, 'engineyard/api'
11
- autoload :Collection, 'engineyard/collection'
12
- autoload :Model, 'engineyard/model'
13
10
  autoload :Repo, 'engineyard/repo'
14
- autoload :Resolver, 'engineyard/resolver'
15
-
16
- class UI
17
- # stub debug outside of the CLI
18
- def debug(*); end
19
- end
20
-
21
- def self.ui
22
- @ui ||= UI.new
23
- end
24
-
25
- def self.ui=(ui)
26
- @ui = ui
27
- end
28
-
29
- def self.config
30
- @config ||= EY::Config.new
31
- end
32
11
  end
@@ -1,6 +1,8 @@
1
1
  require 'engineyard'
2
2
  require 'engineyard/error'
3
3
  require 'engineyard/thor'
4
+ require 'engineyard/deploy_config'
5
+ require 'engineyard/serverside_runner'
4
6
 
5
7
  module EY
6
8
  class CLI < EY::Thor
@@ -8,13 +10,25 @@ module EY
8
10
  require 'engineyard/cli/web'
9
11
  require 'engineyard/cli/api'
10
12
  require 'engineyard/cli/ui'
13
+ require 'engineyard/error'
14
+ require 'engineyard-cloud-client/errors'
11
15
 
12
16
  include Thor::Actions
13
17
 
14
18
  def self.start(*)
15
- Thor::Base.shell = EY::CLI::UI
16
- EY.ui = EY::CLI::UI.new
19
+ ui = EY::CLI::UI.new
17
20
  super
21
+ rescue EY::Error, EY::CloudClient::Error => e
22
+ ui.print_exception(e)
23
+ raise
24
+ rescue Interrupt => e
25
+ puts
26
+ ui.print_exception(e)
27
+ ui.say("Quitting...")
28
+ raise
29
+ rescue Exception => e
30
+ ui.print_exception(e)
31
+ raise
18
32
  end
19
33
 
20
34
  desc "deploy [--environment ENVIRONMENT] [--ref GIT-REF]",
@@ -23,67 +37,95 @@ module EY
23
37
  This command must be run with the current directory containing the app to be
24
38
  deployed. If ey.yml specifies a default branch then the ref parameter can be
25
39
  omitted. Furthermore, if a default branch is specified but a different command
26
- is supplied the deploy will fail unless --ignore-default-branch is used.
40
+ is supplied the deploy will fail unless -R or --force-ref is used.
27
41
 
28
- Migrations are run based on the 'Migrate?' setting you define in your dashboard
29
- for the application. If you want to override these settings, a different
30
- command can be specified via --migrate "ruby do_migrations.rb". Migrations
31
- can also be skipped entirely by using --no-migrate.
42
+ Migrations are run based on the settings in your ey.yml file.
43
+ With each deploy the default migration setting can be overriden by
44
+ specifying --migrate or --migrate 'rake db:migrate'.
45
+ Migrations can also be skipped by using --no-migrate.
32
46
  DESC
33
- method_option :force_ref, :type => :string, :aliases => %w(--ignore-default-branch -R),
34
- :lazy_default => true,
35
- :desc => "Force a deploy of the specified git ref even if a default is set in ey.yml."
36
- method_option :ignore_bad_master, :type => :boolean,
47
+ method_option :ignore_bad_master, :type => :boolean, :aliases => %w(--ignore-bad-bridge),
37
48
  :desc => "Force a deploy even if the master is in a bad state"
38
49
  method_option :migrate, :type => :string, :aliases => %w(-m),
39
50
  :lazy_default => true,
40
- :desc => "Run migrations via [MIGRATE], defaults to 'rake db:migrate'; use --no-migrate to avoid running migrations"
41
- method_option :environment, :type => :string, :aliases => %w(-e),
42
- :desc => "Environment in which to deploy this application"
51
+ :desc => "Run migrations via [MIGRATE], defaults to '#{EY::DeployConfig::Migrate::DEFAULT}'; use --no-migrate to avoid running migrations"
43
52
  method_option :ref, :type => :string, :aliases => %w(-r --branch --tag),
53
+ :required => true, :default => '',
44
54
  :desc => "Git ref to deploy. May be a branch, a tag, or a SHA. Use -R to deploy a different ref if a default is set."
55
+ method_option :force_ref, :type => :string, :aliases => %w(--ignore-default-branch -R),
56
+ :lazy_default => true,
57
+ :desc => "Force a deploy of the specified git ref even if a default is set in ey.yml."
58
+ method_option :environment, :type => :string, :aliases => %w(-e),
59
+ :required => true, :default => false,
60
+ :desc => "Environment in which to deploy this application"
45
61
  method_option :app, :type => :string, :aliases => %w(-a),
62
+ :required => true, :default => '',
46
63
  :desc => "Name of the application to deploy"
47
64
  method_option :account, :type => :string, :aliases => %w(-c),
65
+ :required => true, :default => '',
48
66
  :desc => "Name of the account in which the environment can be found"
49
67
  method_option :verbose, :type => :boolean, :aliases => %w(-v),
50
68
  :desc => "Be verbose"
51
69
  method_option :extra_deploy_hook_options, :type => :hash, :default => {},
52
70
  :desc => "Additional options to be made available in deploy hooks (in the 'config' hash)"
53
71
  def deploy
54
- EY.ui.info "Loading application data from EY Cloud..."
55
-
56
- app, environment = fetch_app_and_environment(options[:app], options[:environment], options[:account])
57
- environment.ignore_bad_master = options[:ignore_bad_master]
58
- deploy_ref = if options[:app]
59
- environment.resolve_branch(options[:ref], options[:force_ref]) ||
60
- raise(EY::Error, "When specifying the application, you must also specify the ref to deploy\nUsage: ey deploy --app <app name> --ref <branch|tag|ref>")
61
- else
62
- environment.resolve_branch(options[:ref], options[:force_ref]) ||
63
- repo.current_branch ||
64
- raise(DeployArgumentError)
65
- end
66
-
67
- EY.ui.info "Beginning deploy of ref '#{deploy_ref}' for '#{app.name}' in '#{environment.name}' on server..."
68
-
69
- deploy_options = {'extras' => {'deployed_by' => api.user.name}.merge(options[:extra_deploy_hook_options])}
70
- if options.has_key?('migrate') # thor set migrate => nil when --no-migrate
71
- deploy_options['migrate'] = options['migrate'].respond_to?(:to_str) ? options['migrate'] : !!options['migrate']
72
- end
73
- deploy_options['verbose'] = options['verbose'] if options.has_key?('verbose')
74
-
75
-
76
- if environment.deploy(app, deploy_ref, deploy_options)
77
- EY.ui.info "Deploy complete"
78
- EY.ui.info "Now you can run `ey launch' to open the application in a browser."
79
- else
80
- raise EY::Error, "Deploy failed"
72
+ ui.info "Loading application data from EY Cloud..."
73
+
74
+ app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
75
+ app_env.environment.ignore_bad_bridge = options[:ignore_bad_master]
76
+
77
+ env_config = config.environment_config(app_env.environment_name)
78
+ deploy_config = EY::DeployConfig.new(options, env_config, repo, ui)
79
+
80
+ deployment = app_env.new_deployment({
81
+ :ref => deploy_config.ref,
82
+ :migrate => deploy_config.migrate,
83
+ :migrate_command => deploy_config.migrate_command,
84
+ :extra_config => deploy_config.extra_config,
85
+ })
86
+
87
+ runner = serverside_runner(app_env, deploy_config.verbose)
88
+
89
+ out = EY::CLI::UI::Tee.new(ui.out, deployment.out)
90
+ err = EY::CLI::UI::Tee.new(ui.err, deployment.err)
91
+
92
+ ui.info "Beginning deploy..."
93
+ begin
94
+ deployment.start
95
+ ui.show_deployment(deployment)
96
+ out << "Deploy initiated.\n"
97
+
98
+ runner.deploy do |args|
99
+ args.config = deployment.config if deployment.config
100
+ if deployment.migrate
101
+ args.migrate = deployment.migrate_command
102
+ else
103
+ args.migrate = false
104
+ end
105
+ args.ref = deployment.resolved_ref
106
+ end
107
+ deployment.successful = runner.call(out, err)
108
+ rescue Interrupt
109
+ err << "Interrupted. Deployment halted.\n"
110
+ ui.warn "Recording canceled deployment in EY Cloud..."
111
+ ui.warn "WARNING: Interrupting again may result in a never-finished deployment in the deployment history on EY Cloud."
112
+ raise
113
+ rescue StandardError => e
114
+ deployment.err << "Error encountered during deploy.\n#{e.class} #{e}\n"
115
+ ui.print_exception(e)
116
+ raise
117
+ ensure
118
+ deployment.finished
119
+
120
+ if deployment.successful?
121
+ ui.info "Successful deployment recorded on EY Cloud"
122
+ ui.info "Deploy complete"
123
+ ui.info "Now you can run `ey launch' to open the application in a browser."
124
+ else
125
+ ui.info "Failed deployment recorded on EY Cloud"
126
+ raise EY::Error, "Deploy failed"
127
+ end
81
128
  end
82
-
83
- rescue NoEnvironmentError => e
84
- # Give better feedback about why we couldn't find the environment.
85
- exists = api.environments.named(options[:environment])
86
- raise exists ? EnvironmentUnlinkedError.new(options[:environment]) : e
87
129
  end
88
130
 
89
131
  desc "status", "Show the deployment status of the app"
@@ -92,18 +134,25 @@ module EY
92
134
  application and environment.
93
135
  DESC
94
136
  method_option :environment, :type => :string, :aliases => %w(-e),
137
+ :required => true, :default => '',
95
138
  :desc => "Environment where the application is deployed"
96
139
  method_option :app, :type => :string, :aliases => %w(-a),
140
+ :required => true, :default => '',
97
141
  :desc => "Name of the application"
98
142
  method_option :account, :type => :string, :aliases => %w(-c),
143
+ :required => true, :default => '',
99
144
  :desc => "Name of the account in which the application can be found"
100
145
  def status
101
- app, environment = fetch_app_and_environment(options[:app], options[:environment], options[:account])
102
- deployment = app.last_deployment_on(environment)
146
+ app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
147
+ deployment = app_env.last_deployment
103
148
  if deployment
104
- EY.ui.show_deployment(deployment)
149
+ ui.say "# Status of last deployment of #{app_env.to_hierarchy_str}:"
150
+ ui.say "#"
151
+ ui.show_deployment(deployment)
152
+ ui.say "#"
153
+ ui.deployment_result(deployment)
105
154
  else
106
- raise EY::Error, "Application #{app.name} hass not been deployed on #{environment.name}."
155
+ raise EY::Error, "Application #{app_env.app.name} has not been deployed on #{app_env.environment.name}."
107
156
  end
108
157
  end
109
158
 
@@ -113,27 +162,55 @@ module EY
113
162
  display all environments, including those for this app.
114
163
  DESC
115
164
 
116
- method_option :all, :type => :boolean, :aliases => %(-a)
117
- method_option :simple, :type => :boolean, :aliases => %(-s)
165
+ method_option :all, :type => :boolean, :aliases => %(-A),
166
+ :desc => "Show all environments (ignores --app, --account, and --environment arguments)"
167
+ method_option :simple, :type => :boolean, :aliases => %(-s),
168
+ :desc => "Display one environment per line with no extra output"
169
+ method_option :app, :type => :string, :aliases => %w(-a),
170
+ :required => true, :default => '',
171
+ :desc => "Show environments for this application"
172
+ method_option :account, :type => :string, :aliases => %w(-c),
173
+ :required => true, :default => '',
174
+ :desc => "Show environments in this account"
175
+ method_option :environment, :type => :string, :aliases => %w(-e),
176
+ :required => true, :default => '',
177
+ :desc => "Show environments matching environment name"
118
178
  def environments
119
179
  if options[:all] && options[:simple]
120
180
  # just put each env
121
181
  puts api.environments.map {|env| env.name}
122
182
  elsif options[:all]
123
- EY.ui.print_envs(api.apps, EY.config.default_environment, options[:simple])
183
+ ui.print_envs(api.apps, config.default_environment, options[:simple], config.endpoint)
124
184
  else
125
- apps = api.apps_for_repo(repo)
185
+ remotes = nil
186
+ if options[:app] == ''
187
+ repo.fail_on_no_remotes!
188
+ remotes = repo.remotes
189
+ end
190
+
191
+ resolver = api.resolve_app_environments({
192
+ :account_name => options[:account],
193
+ :app_name => options[:app],
194
+ :environment_name => options[:environment],
195
+ :remotes => remotes,
196
+ })
197
+
198
+ resolver.no_matches do |errors|
199
+ messages = errors
200
+ messages << "Use #{self.class.send(:banner_base)} environments --all to see all environments."
201
+ raise EY::NoMatchesError.new(messages.join("\n"))
202
+ end
203
+
204
+ apps = resolver.matches.map { |app_env| app_env.app }.uniq
126
205
 
127
206
  if apps.size > 1
128
207
  message = "This git repo matches multiple Applications in EY Cloud:\n"
129
208
  apps.each { |app| message << "\t#{app.name}\n" }
130
209
  message << "The following environments contain those applications:\n\n"
131
- EY.ui.warn(message)
132
- elsif apps.empty?
133
- EY.ui.warn(NoAppError.new(repo).message + "\nUse #{self.class.send(:banner_base)} environments --all to see all environments.")
210
+ ui.warn(message)
134
211
  end
135
212
 
136
- EY.ui.print_envs(apps, EY.config.default_environment, options[:simple])
213
+ ui.print_envs(apps, config.default_environment, options[:simple], config.endpoint)
137
214
  end
138
215
  end
139
216
  map "envs" => :environments
@@ -150,12 +227,14 @@ module EY
150
227
  DESC
151
228
 
152
229
  method_option :environment, :type => :string, :aliases => %w(-e),
230
+ :required => true, :default => '',
153
231
  :desc => "Environment to rebuild"
154
232
  method_option :account, :type => :string, :aliases => %w(-c),
233
+ :required => true, :default => '',
155
234
  :desc => "Name of the account in which the environment can be found"
156
235
  def rebuild
157
236
  environment = fetch_environment(options[:environment], options[:account])
158
- EY.ui.debug("Rebuilding #{environment.name}")
237
+ ui.debug("Rebuilding #{environment.name}")
159
238
  environment.rebuild
160
239
  end
161
240
  map "update" => :rebuild
@@ -167,21 +246,32 @@ module EY
167
246
  DESC
168
247
 
169
248
  method_option :environment, :type => :string, :aliases => %w(-e),
249
+ :required => true, :default => '',
170
250
  :desc => "Environment in which to roll back the application"
171
251
  method_option :app, :type => :string, :aliases => %w(-a),
252
+ :required => true, :default => '',
172
253
  :desc => "Name of the application to roll back"
173
254
  method_option :account, :type => :string, :aliases => %w(-c),
255
+ :required => true, :default => '',
174
256
  :desc => "Name of the account in which the environment can be found"
175
257
  method_option :verbose, :type => :boolean, :aliases => %w(-v),
176
258
  :desc => "Be verbose"
177
259
  method_option :extra_deploy_hook_options, :type => :hash, :default => {},
178
260
  :desc => "Additional options to be made available in deploy hooks (in the 'config' hash)"
179
261
  def rollback
180
- app, environment = fetch_app_and_environment(options[:app], options[:environment], options[:account])
262
+ app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
263
+ env_config = config.environment_config(app_env.environment_name)
264
+ deploy_config = EY::DeployConfig.new(options, env_config, repo, ui)
265
+
266
+ ui.info("Rolling back #{app_env.to_hierarchy_str}")
181
267
 
182
- EY.ui.info("Rolling back '#{app.name}' in '#{environment.name}'")
183
- if environment.rollback(app, options[:extra_deploy_hook_options], options[:verbose])
184
- EY.ui.info "Rollback complete"
268
+ runner = serverside_runner(app_env, deploy_config.verbose)
269
+ runner.rollback do |args|
270
+ args.config = deploy_config.extra_config if deploy_config.extra_config
271
+ end
272
+
273
+ if runner.call(ui.out, ui.err)
274
+ ui.info "Rollback complete"
185
275
  else
186
276
  raise EY::Error, "Rollback failed"
187
277
  end
@@ -199,8 +289,10 @@ module EY
199
289
  $ #{banner_base} ssh "rm -f /some/file" -e my-environment --all
200
290
  DESC
201
291
  method_option :environment, :type => :string, :aliases => %w(-e),
292
+ :required => true, :default => '',
202
293
  :desc => "Environment to ssh into"
203
294
  method_option :account, :type => :string, :aliases => %w(-c),
295
+ :required => true, :default => '',
204
296
  :desc => "Name of the account in which the environment can be found"
205
297
  method_option :all, :type => :boolean, :aliases => %(-a),
206
298
  :desc => "Run command on all servers"
@@ -235,8 +327,7 @@ module EY
235
327
  return lambda {|instance| %w(solo db_master db_slave).include?(instance.role) } if opts[:db_servers ]
236
328
  return lambda {|instance| %w(solo db_master ).include?(instance.role) } if opts[:db_master ]
237
329
  return lambda {|instance| %w(db_slave ).include?(instance.role) } if opts[:db_slaves ]
238
- return lambda {|instance| %w(util ).include?(instance.role) &&
239
- opts[:utilities].include?(instance.name) } if opts[:utilities ]
330
+ return lambda {|instance| %w(util).include?(instance.role) && opts[:utilities].include?(instance.name) } if opts[:utilities]
240
331
  return lambda {|instance| %w(solo app_master ).include?(instance.role) }
241
332
  end
242
333
 
@@ -264,22 +355,24 @@ module EY
264
355
  displayed beneath the main configuration logs.
265
356
  DESC
266
357
  method_option :environment, :type => :string, :aliases => %w(-e),
358
+ :required => true, :default => '',
267
359
  :desc => "Environment with the interesting logs"
268
360
  method_option :account, :type => :string, :aliases => %w(-c),
361
+ :required => true, :default => '',
269
362
  :desc => "Name of the account in which the environment can be found"
270
363
  def logs
271
364
  environment = fetch_environment(options[:environment], options[:account])
272
365
  environment.logs.each do |log|
273
- EY.ui.info log.instance_name
366
+ ui.info log.instance_name
274
367
 
275
368
  if log.main
276
- EY.ui.info "Main logs for #{environment.name}:"
277
- EY.ui.say log.main
369
+ ui.info "Main logs for #{environment.name}:"
370
+ ui.say log.main
278
371
  end
279
372
 
280
373
  if log.custom
281
- EY.ui.info "Custom logs for #{environment.name}:"
282
- EY.ui.say log.custom
374
+ ui.info "Custom logs for #{environment.name}:"
375
+ ui.say log.custom
283
376
  end
284
377
  end
285
378
  end
@@ -292,7 +385,7 @@ module EY
292
385
 
293
386
  desc "version", "Print version number."
294
387
  def version
295
- EY.ui.say %{engineyard version #{EY::VERSION}}
388
+ ui.say %{engineyard version #{EY::VERSION}}
296
389
  end
297
390
  map ["-v", "--version"] => :version
298
391
 
@@ -302,38 +395,38 @@ module EY
302
395
  base = self.class.send(:banner_base)
303
396
  list = self.class.printable_tasks
304
397
 
305
- EY.ui.say "Usage:"
306
- EY.ui.say " #{base} [--help] [--version] COMMAND [ARGS]"
307
- EY.ui.say
398
+ ui.say "Usage:"
399
+ ui.say " #{base} [--help] [--version] COMMAND [ARGS]"
400
+ ui.say
308
401
 
309
- EY.ui.say "Deploy commands:"
402
+ ui.say "Deploy commands:"
310
403
  deploy_cmds = %w(deploy environments logs rebuild rollback status)
311
404
  deploy_cmds.map! do |name|
312
405
  list.find{|task| task[0] =~ /^#{base} #{name}/ }
313
406
  end
314
407
  list -= deploy_cmds
315
408
 
316
- EY.ui.print_help(deploy_cmds)
317
- EY.ui.say
409
+ ui.print_help(deploy_cmds)
410
+ ui.say
318
411
 
319
412
  self.class.subcommands.each do |name|
320
413
  klass = self.class.subcommand_class_for(name)
321
414
  list.reject!{|cmd| cmd[0] =~ /^#{base} #{name}/}
322
- EY.ui.say "#{name.capitalize} commands:"
415
+ ui.say "#{name.capitalize} commands:"
323
416
  tasks = klass.printable_tasks.reject{|t| t[0] =~ /help$/ }
324
- EY.ui.print_help(tasks)
325
- EY.ui.say
417
+ ui.print_help(tasks)
418
+ ui.say
326
419
  end
327
420
 
328
421
  %w(help version).each{|n| list.reject!{|c| c[0] =~ /^#{base} #{n}/ } }
329
422
  if list.any?
330
- EY.ui.say "Other commands:"
331
- EY.ui.print_help(list)
332
- EY.ui.say
423
+ ui.say "Other commands:"
424
+ ui.print_help(list)
425
+ ui.say
333
426
  end
334
427
 
335
428
  self.class.send(:class_options_help, shell)
336
- EY.ui.say "See '#{base} help COMMAND' for more information on a specific command."
429
+ ui.say "See '#{base} help COMMAND' for more information on a specific command."
337
430
  elsif klass = self.class.subcommand_class_for(cmds.first)
338
431
  klass.new.help(*cmds[1..-1])
339
432
  else
@@ -341,20 +434,25 @@ module EY
341
434
  end
342
435
  end
343
436
 
344
- desc "launch [--environment ENVIRONMENT] [--account ACCOUNT]", "Open application in browser."
437
+ desc "launch [--app APP] [--environment ENVIRONMENT] [--account ACCOUNT]", "Open application in browser."
345
438
  method_option :environment, :type => :string, :aliases => %w(-e),
346
- :desc => "Name of the environment"
439
+ :required => true, :default => '',
440
+ :desc => "Environment where the application is deployed"
441
+ method_option :app, :type => :string, :aliases => %w(-a),
442
+ :required => true, :default => '',
443
+ :desc => "Name of the application"
347
444
  method_option :account, :type => :string, :aliases => %w(-c),
348
- :desc => "Name of the account in which the environment can be found"
445
+ :required => true, :default => '',
446
+ :desc => "Name of the account in which the application can be found"
349
447
  def launch
350
- environment = fetch_environment(options[:environment], options[:account])
351
- environment.launch
448
+ app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
449
+ Launchy.open(app_env.uri)
352
450
  end
353
451
 
354
452
  desc "whoami", "Who am I logged in as?"
355
453
  def whoami
356
- user = api.user
357
- EY.ui.say "#{user.name} (#{user.email})"
454
+ current_user = api.current_user
455
+ ui.say "#{current_user.name} (#{current_user.email})"
358
456
  end
359
457
 
360
458
  desc "login", "Log in and verify access to EY Cloud."
@@ -366,10 +464,10 @@ module EY
366
464
  def logout
367
465
  eyrc = EYRC.load
368
466
  if eyrc.delete_api_token
369
- EY.ui.say "API token removed: #{eyrc.path}"
370
- EY.ui.say "Run any other command to login again."
467
+ ui.say "API token removed: #{eyrc.path}"
468
+ ui.say "Run any other command to login again."
371
469
  else
372
- EY.ui.say "Already logged out. Run any other command to login again."
470
+ ui.say "Already logged out. Run any other command to login again."
373
471
  end
374
472
  end
375
473