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