crazy-yard 3.2.2
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.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +438 -0
- data/bin/ey +9 -0
- data/lib/engineyard.rb +9 -0
- data/lib/engineyard/cli.rb +816 -0
- data/lib/engineyard/cli/api.rb +98 -0
- data/lib/engineyard/cli/recipes.rb +129 -0
- data/lib/engineyard/cli/ui.rb +275 -0
- data/lib/engineyard/cli/web.rb +85 -0
- data/lib/engineyard/config.rb +158 -0
- data/lib/engineyard/deploy_config.rb +65 -0
- data/lib/engineyard/deploy_config/ref.rb +56 -0
- data/lib/engineyard/error.rb +82 -0
- data/lib/engineyard/eyrc.rb +59 -0
- data/lib/engineyard/repo.rb +105 -0
- data/lib/engineyard/serverside_runner.rb +159 -0
- data/lib/engineyard/templates.rb +6 -0
- data/lib/engineyard/templates/ey.yml.erb +196 -0
- data/lib/engineyard/templates/ey_yml.rb +119 -0
- data/lib/engineyard/thor.rb +215 -0
- data/lib/engineyard/version.rb +4 -0
- data/lib/vendor/thor/Gemfile +15 -0
- data/lib/vendor/thor/LICENSE.md +20 -0
- data/lib/vendor/thor/README.md +35 -0
- data/lib/vendor/thor/lib/thor.rb +473 -0
- data/lib/vendor/thor/lib/thor/actions.rb +318 -0
- data/lib/vendor/thor/lib/thor/actions/create_file.rb +105 -0
- data/lib/vendor/thor/lib/thor/actions/create_link.rb +60 -0
- data/lib/vendor/thor/lib/thor/actions/directory.rb +119 -0
- data/lib/vendor/thor/lib/thor/actions/empty_directory.rb +137 -0
- data/lib/vendor/thor/lib/thor/actions/file_manipulation.rb +314 -0
- data/lib/vendor/thor/lib/thor/actions/inject_into_file.rb +109 -0
- data/lib/vendor/thor/lib/thor/base.rb +652 -0
- data/lib/vendor/thor/lib/thor/command.rb +136 -0
- data/lib/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +80 -0
- data/lib/vendor/thor/lib/thor/core_ext/io_binary_read.rb +12 -0
- data/lib/vendor/thor/lib/thor/core_ext/ordered_hash.rb +100 -0
- data/lib/vendor/thor/lib/thor/error.rb +28 -0
- data/lib/vendor/thor/lib/thor/group.rb +282 -0
- data/lib/vendor/thor/lib/thor/invocation.rb +172 -0
- data/lib/vendor/thor/lib/thor/parser.rb +4 -0
- data/lib/vendor/thor/lib/thor/parser/argument.rb +74 -0
- data/lib/vendor/thor/lib/thor/parser/arguments.rb +171 -0
- data/lib/vendor/thor/lib/thor/parser/option.rb +121 -0
- data/lib/vendor/thor/lib/thor/parser/options.rb +218 -0
- data/lib/vendor/thor/lib/thor/rake_compat.rb +72 -0
- data/lib/vendor/thor/lib/thor/runner.rb +322 -0
- data/lib/vendor/thor/lib/thor/shell.rb +88 -0
- data/lib/vendor/thor/lib/thor/shell/basic.rb +393 -0
- data/lib/vendor/thor/lib/thor/shell/color.rb +148 -0
- data/lib/vendor/thor/lib/thor/shell/html.rb +127 -0
- data/lib/vendor/thor/lib/thor/util.rb +270 -0
- data/lib/vendor/thor/lib/thor/version.rb +3 -0
- data/lib/vendor/thor/thor.gemspec +24 -0
- data/spec/engineyard/cli/api_spec.rb +50 -0
- data/spec/engineyard/cli_spec.rb +28 -0
- data/spec/engineyard/config_spec.rb +61 -0
- data/spec/engineyard/deploy_config_spec.rb +194 -0
- data/spec/engineyard/eyrc_spec.rb +76 -0
- data/spec/engineyard/repo_spec.rb +83 -0
- data/spec/engineyard_spec.rb +7 -0
- data/spec/ey/console_spec.rb +57 -0
- data/spec/ey/deploy_spec.rb +435 -0
- data/spec/ey/ey_spec.rb +23 -0
- data/spec/ey/init_spec.rb +123 -0
- data/spec/ey/list_environments_spec.rb +120 -0
- data/spec/ey/login_spec.rb +33 -0
- data/spec/ey/logout_spec.rb +24 -0
- data/spec/ey/logs_spec.rb +36 -0
- data/spec/ey/rebuild_spec.rb +18 -0
- data/spec/ey/recipes/apply_spec.rb +29 -0
- data/spec/ey/recipes/download_spec.rb +43 -0
- data/spec/ey/recipes/upload_spec.rb +99 -0
- data/spec/ey/rollback_spec.rb +73 -0
- data/spec/ey/scp_spec.rb +176 -0
- data/spec/ey/servers_spec.rb +209 -0
- data/spec/ey/ssh_spec.rb +273 -0
- data/spec/ey/status_spec.rb +45 -0
- data/spec/ey/timeout_deploy_spec.rb +18 -0
- data/spec/ey/web/disable_spec.rb +21 -0
- data/spec/ey/web/enable_spec.rb +26 -0
- data/spec/ey/web/restart_spec.rb +21 -0
- data/spec/ey/whoami_spec.rb +30 -0
- data/spec/spec_helper.rb +84 -0
- data/spec/support/bundled_ey +7 -0
- data/spec/support/fixture_recipes.tgz +0 -0
- data/spec/support/git_repos.rb +115 -0
- data/spec/support/helpers.rb +330 -0
- data/spec/support/matchers.rb +16 -0
- data/spec/support/ruby_ext.rb +13 -0
- data/spec/support/shared_behavior.rb +278 -0
- metadata +411 -0
data/bin/ey
ADDED
data/lib/engineyard.rb
ADDED
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
require 'engineyard'
|
|
2
|
+
require 'engineyard/error'
|
|
3
|
+
require 'engineyard/thor'
|
|
4
|
+
require 'engineyard/deploy_config'
|
|
5
|
+
require 'engineyard/serverside_runner'
|
|
6
|
+
require 'launchy'
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
|
|
9
|
+
module EY
|
|
10
|
+
class CLI < EY::Thor
|
|
11
|
+
require 'engineyard/cli/recipes'
|
|
12
|
+
require 'engineyard/cli/web'
|
|
13
|
+
require 'engineyard/cli/api'
|
|
14
|
+
require 'engineyard/cli/ui'
|
|
15
|
+
require 'engineyard/error'
|
|
16
|
+
require 'engineyard-cloud-client/errors'
|
|
17
|
+
|
|
18
|
+
include Thor::Actions
|
|
19
|
+
|
|
20
|
+
def self.start(given_args=ARGV, config={})
|
|
21
|
+
Thor::Base.shell = EY::CLI::UI
|
|
22
|
+
ui = EY::CLI::UI.new
|
|
23
|
+
super(given_args, {shell: ui}.merge(config))
|
|
24
|
+
rescue Thor::Error, EY::Error, EY::CloudClient::Error => e
|
|
25
|
+
ui.print_exception(e)
|
|
26
|
+
raise
|
|
27
|
+
rescue Interrupt => e
|
|
28
|
+
puts
|
|
29
|
+
ui.print_exception(e)
|
|
30
|
+
ui.say("Quitting...")
|
|
31
|
+
raise
|
|
32
|
+
rescue SystemExit, Errno::EPIPE
|
|
33
|
+
# don't print a message for safe exits
|
|
34
|
+
raise
|
|
35
|
+
rescue Exception => e
|
|
36
|
+
ui.print_exception(e)
|
|
37
|
+
raise
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class_option :api_token, type: :string, desc: "Use API_TOKEN to authenticate this command"
|
|
41
|
+
class_option :serverside_version, type: :string, desc: "Please use with care! Override deploy system version (same as ENV variable ENGINEYARD_SERVERSIDE_VERSION)"
|
|
42
|
+
class_option :quiet, aliases: %w[-q], type: :boolean, desc: "Quieter CLI output."
|
|
43
|
+
|
|
44
|
+
desc "init",
|
|
45
|
+
"Initialize the current directory with an ey.yml configuration file."
|
|
46
|
+
long_desc <<-DESC
|
|
47
|
+
Initialize the current directory with an ey.yml configuration file.
|
|
48
|
+
|
|
49
|
+
Please read the generated file and make adjustments.
|
|
50
|
+
Many applications will need only the default behavior.
|
|
51
|
+
For reference, many available options are explained in the generated file.
|
|
52
|
+
|
|
53
|
+
IMPORTANT: THE GENERATED FILE '#{EY::Config.pathname_for_write}'
|
|
54
|
+
MUST BE COMMITTED TO YOUR REPOSITORY OR OPTIONS WILL NOT BE LOADED.
|
|
55
|
+
DESC
|
|
56
|
+
method_option :path, type: :string, aliases: %w(-p),
|
|
57
|
+
desc: "Path for ey.yml (supported paths: #{EY::Config::CONFIG_FILES.join(', ')})"
|
|
58
|
+
def init
|
|
59
|
+
unless EY::Repo.exist?
|
|
60
|
+
raise EY::Error, "Working directory is not a repository. Aborting."
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
path = Pathname.new(options['path'] || EY::Config.pathname_for_write)
|
|
64
|
+
|
|
65
|
+
existing = {}
|
|
66
|
+
if path.exist?
|
|
67
|
+
ui.warn "Reinitializing existing file: #{path}"
|
|
68
|
+
existing = EY::Config.load_config
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
template = EY::Templates::EyYml.new(existing)
|
|
72
|
+
template.write(path)
|
|
73
|
+
|
|
74
|
+
ui.info <<-GIT
|
|
75
|
+
|
|
76
|
+
Configuration generated: #{path}
|
|
77
|
+
Go look at it, then add it to your repository!
|
|
78
|
+
|
|
79
|
+
\tgit add #{path}
|
|
80
|
+
\tgit commit -m "Add generated #{path} from ey init"
|
|
81
|
+
|
|
82
|
+
GIT
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
desc "deploy [--environment ENVIRONMENT] [--ref GIT-REF]",
|
|
87
|
+
"Deploy specified branch, tag, or sha to specified environment."
|
|
88
|
+
long_desc <<-DESC
|
|
89
|
+
This command must be run with the current directory containing the app to be
|
|
90
|
+
deployed. If ey.yml specifies a default branch then the ref parameter can be
|
|
91
|
+
omitted. Furthermore, if a default branch is specified but a different command
|
|
92
|
+
is supplied the deploy will fail unless -R or --force-ref is used.
|
|
93
|
+
|
|
94
|
+
Migrations are run based on the settings in your ey.yml file.
|
|
95
|
+
With each deploy the default migration setting can be overriden by
|
|
96
|
+
specifying --migrate or --migrate 'rake db:migrate'.
|
|
97
|
+
Migrations can also be skipped by using --no-migrate.
|
|
98
|
+
DESC
|
|
99
|
+
method_option :ignore_bad_master, type: :boolean, aliases: %w(--ignore-bad-bridge),
|
|
100
|
+
desc: "Force a deploy even if the master is in a bad state"
|
|
101
|
+
method_option :migrate, type: :string, aliases: %w(-m),
|
|
102
|
+
lazy_default: true,
|
|
103
|
+
desc: "Run migrations via [MIGRATE]; use --no-migrate to avoid running migrations"
|
|
104
|
+
method_option :ref, type: :string, aliases: %w(-r --branch --tag),
|
|
105
|
+
required: true, default: '',
|
|
106
|
+
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."
|
|
107
|
+
method_option :force_ref, type: :string, aliases: %w(--ignore-default-branch -R),
|
|
108
|
+
lazy_default: true,
|
|
109
|
+
desc: "Force a deploy of the specified git ref even if a default is set in ey.yml."
|
|
110
|
+
method_option :environment, type: :string, aliases: %w(-e),
|
|
111
|
+
required: true, default: false,
|
|
112
|
+
desc: "Environment in which to deploy this application"
|
|
113
|
+
method_option :app, type: :string, aliases: %w(-a),
|
|
114
|
+
required: true, default: '',
|
|
115
|
+
desc: "Name of the application to deploy"
|
|
116
|
+
method_option :account, type: :string, aliases: %w(-c),
|
|
117
|
+
required: true, default: '',
|
|
118
|
+
desc: "Name of the account in which the environment can be found"
|
|
119
|
+
method_option :verbose, type: :boolean, aliases: %w(-v),
|
|
120
|
+
desc: "Be verbose"
|
|
121
|
+
method_option :config, type: :hash, default: {}, aliases: %w(--extra-deploy-hook-options),
|
|
122
|
+
desc: "Hash made available in deploy hooks (in the 'config' hash), can also override some ey.yml settings."
|
|
123
|
+
def deploy
|
|
124
|
+
app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
|
|
125
|
+
|
|
126
|
+
env_config = config.environment_config(app_env.environment_name)
|
|
127
|
+
deploy_config = EY::DeployConfig.new(options, env_config, repo, ui)
|
|
128
|
+
|
|
129
|
+
deployment = app_env.new_deployment({
|
|
130
|
+
ref: deploy_config.ref,
|
|
131
|
+
migrate: deploy_config.migrate,
|
|
132
|
+
migrate_command: deploy_config.migrate_command,
|
|
133
|
+
extra_config: deploy_config.extra_config,
|
|
134
|
+
serverside_version: serverside_version,
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
runner = serverside_runner(app_env, deploy_config.verbose, deployment.serverside_version, options[:ignore_bad_master])
|
|
138
|
+
|
|
139
|
+
out = EY::CLI::UI::Tee.new(ui.out, deployment.output)
|
|
140
|
+
err = EY::CLI::UI::Tee.new(ui.err, deployment.output)
|
|
141
|
+
|
|
142
|
+
ui.info "Beginning deploy...", :green
|
|
143
|
+
begin
|
|
144
|
+
deployment.start
|
|
145
|
+
rescue
|
|
146
|
+
ui.error "Error encountered before deploy. Deploy not started."
|
|
147
|
+
raise
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
begin
|
|
151
|
+
ui.show_deployment(deployment)
|
|
152
|
+
out << "Deploy initiated.\n"
|
|
153
|
+
|
|
154
|
+
runner.deploy do |args|
|
|
155
|
+
args.config = deployment.config if deployment.config
|
|
156
|
+
if deployment.migrate
|
|
157
|
+
args.migrate = deployment.migrate_command
|
|
158
|
+
else
|
|
159
|
+
args.migrate = false
|
|
160
|
+
end
|
|
161
|
+
args.ref = deployment.resolved_ref
|
|
162
|
+
end
|
|
163
|
+
deployment.successful = runner.call(out, err)
|
|
164
|
+
rescue Interrupt
|
|
165
|
+
Signal.trap(:INT) { # The fingers you have used to dial are too fat...
|
|
166
|
+
ui.info "\nRun `ey timeout-deploy` to mark an unfinished deployment as failed."
|
|
167
|
+
exit 1
|
|
168
|
+
}
|
|
169
|
+
err << "Interrupted. Deployment halted.\n"
|
|
170
|
+
ui.warn <<-WARN
|
|
171
|
+
Recording interruption of this unfinished deployment in Engine Yard Cloud...
|
|
172
|
+
|
|
173
|
+
WARNING: Interrupting again may prevent Engine Yard Cloud from recording this
|
|
174
|
+
failed deployment. Unfinished deployments can block future deploys.
|
|
175
|
+
WARN
|
|
176
|
+
raise
|
|
177
|
+
rescue StandardError => e
|
|
178
|
+
deployment.err << "Error encountered during deploy.\n#{e.class} #{e}\n"
|
|
179
|
+
ui.print_exception(e)
|
|
180
|
+
raise
|
|
181
|
+
ensure
|
|
182
|
+
ui.info "Saving log... ", :green
|
|
183
|
+
deployment.finished
|
|
184
|
+
|
|
185
|
+
if deployment.successful?
|
|
186
|
+
ui.info "Successful deployment recorded on Engine Yard Cloud.", :green
|
|
187
|
+
ui.info "Run `ey launch` to open the application in a browser."
|
|
188
|
+
else
|
|
189
|
+
ui.info "Failed deployment recorded on Engine Yard Cloud", :green
|
|
190
|
+
raise EY::Error, "Deploy failed"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
desc "timeout-deploy [--environment ENVIRONMENT]",
|
|
196
|
+
"Fail a stuck unfinished deployment."
|
|
197
|
+
long_desc <<-DESC
|
|
198
|
+
NOTICE: Timing out a deploy does not stop currently running deploy
|
|
199
|
+
processes.
|
|
200
|
+
|
|
201
|
+
This command must be run in the current directory containing the app.
|
|
202
|
+
The latest running deployment will be marked as failed, allowing a
|
|
203
|
+
new deployment to be run. It is possible to mark a potentially successful
|
|
204
|
+
deployment as failed. Only run this when a deployment is known to be
|
|
205
|
+
wrongly unfinished/stuck and when further deployments are blocked.
|
|
206
|
+
DESC
|
|
207
|
+
method_option :environment, type: :string, aliases: %w(-e),
|
|
208
|
+
required: true, default: false,
|
|
209
|
+
desc: "Environment in which to deploy this application"
|
|
210
|
+
method_option :app, type: :string, aliases: %w(-a),
|
|
211
|
+
required: true, default: '',
|
|
212
|
+
desc: "Name of the application to deploy"
|
|
213
|
+
method_option :account, type: :string, aliases: %w(-c),
|
|
214
|
+
required: true, default: '',
|
|
215
|
+
desc: "Name of the account in which the environment can be found"
|
|
216
|
+
def timeout_deploy
|
|
217
|
+
app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
|
|
218
|
+
deployment = app_env.last_deployment
|
|
219
|
+
if deployment && !deployment.finished?
|
|
220
|
+
begin
|
|
221
|
+
ui.info "Marking last deployment failed...", :green
|
|
222
|
+
deployment.timeout
|
|
223
|
+
ui.deployment_status(deployment)
|
|
224
|
+
rescue EY::CloudClient::RequestFailed => e
|
|
225
|
+
ui.error "Error encountered attempting to timeout previous deployment."
|
|
226
|
+
raise
|
|
227
|
+
end
|
|
228
|
+
else
|
|
229
|
+
raise EY::Error, "No unfinished deployment was found for #{app_env.hierarchy_name}."
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
desc "status", "Show the deployment status of the app"
|
|
234
|
+
long_desc <<-DESC
|
|
235
|
+
Show the current status of most recent deployment of the specified
|
|
236
|
+
application and environment.
|
|
237
|
+
DESC
|
|
238
|
+
method_option :environment, type: :string, aliases: %w(-e),
|
|
239
|
+
required: true, default: '',
|
|
240
|
+
desc: "Environment where the application is deployed"
|
|
241
|
+
method_option :app, type: :string, aliases: %w(-a),
|
|
242
|
+
required: true, default: '',
|
|
243
|
+
desc: "Name of the application"
|
|
244
|
+
method_option :account, type: :string, aliases: %w(-c),
|
|
245
|
+
required: true, default: '',
|
|
246
|
+
desc: "Name of the account in which the application can be found"
|
|
247
|
+
def status
|
|
248
|
+
app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
|
|
249
|
+
deployment = app_env.last_deployment
|
|
250
|
+
if deployment
|
|
251
|
+
ui.deployment_status(deployment)
|
|
252
|
+
else
|
|
253
|
+
raise EY::Error, "Application #{app_env.app.name} has not been deployed on #{app_env.environment.name}."
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
desc "environments [--all]", "List environments for this app; use --all to list all environments."
|
|
258
|
+
long_desc <<-DESC
|
|
259
|
+
By default, environments for this app are displayed. The --all option will
|
|
260
|
+
display all environments, including those for this app.
|
|
261
|
+
DESC
|
|
262
|
+
|
|
263
|
+
method_option :all, type: :boolean, aliases: %(-A),
|
|
264
|
+
desc: "Show all environments (ignores --app, --account, and --environment arguments)"
|
|
265
|
+
method_option :simple, type: :boolean, aliases: %(-s),
|
|
266
|
+
desc: "Display one environment per line with no extra output"
|
|
267
|
+
method_option :app, type: :string, aliases: %w(-a),
|
|
268
|
+
required: true, default: '',
|
|
269
|
+
desc: "Show environments for this application"
|
|
270
|
+
method_option :account, type: :string, aliases: %w(-c),
|
|
271
|
+
required: true, default: '',
|
|
272
|
+
desc: "Show environments in this account"
|
|
273
|
+
method_option :environment, type: :string, aliases: %w(-e),
|
|
274
|
+
required: true, default: '',
|
|
275
|
+
desc: "Show environments matching environment name"
|
|
276
|
+
def environments
|
|
277
|
+
if options[:all] && options[:simple]
|
|
278
|
+
ui.print_simple_envs api.environments
|
|
279
|
+
elsif options[:all]
|
|
280
|
+
ui.print_envs api.apps
|
|
281
|
+
else
|
|
282
|
+
remotes = nil
|
|
283
|
+
if options[:app] == ''
|
|
284
|
+
repo.fail_on_no_remotes!
|
|
285
|
+
remotes = repo.remotes
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
resolver = api.resolve_app_environments({
|
|
289
|
+
account_name: options[:account],
|
|
290
|
+
app_name: options[:app],
|
|
291
|
+
environment_name: options[:environment],
|
|
292
|
+
remotes: remotes,
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
resolver.no_matches do |errors|
|
|
296
|
+
messages = errors
|
|
297
|
+
messages << "Use #{self.class.send(:banner_base)} environments --all to see all environments."
|
|
298
|
+
raise EY::NoMatchesError.new(messages.join("\n"))
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
apps = resolver.matches.map { |app_env| app_env.app }.uniq
|
|
302
|
+
|
|
303
|
+
if options[:simple]
|
|
304
|
+
if apps.size > 1
|
|
305
|
+
message = "# This app matches multiple Applications in Engine Yard Cloud:\n"
|
|
306
|
+
apps.each { |app| message << "#\t#{app.name}\n" }
|
|
307
|
+
message << "# The following environments contain those applications:\n\n"
|
|
308
|
+
ui.warn(message)
|
|
309
|
+
end
|
|
310
|
+
ui.print_simple_envs(apps.map{ |app| app.environments }.flatten)
|
|
311
|
+
else
|
|
312
|
+
ui.print_envs(apps, config.default_environment)
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
map "envs" => :environments
|
|
317
|
+
|
|
318
|
+
desc "servers", "List servers for an environment."
|
|
319
|
+
long_desc <<-DESC
|
|
320
|
+
Display a list of all servers on an environment.
|
|
321
|
+
Specify -s (--simple) to make parsing the output easier
|
|
322
|
+
or -uS (--user --host) to output bash loop friendly "user@hostname"
|
|
323
|
+
DESC
|
|
324
|
+
|
|
325
|
+
method_option :simple, type: :boolean, aliases: %(-s),
|
|
326
|
+
desc: "Display all information in a simplified format without extra text or column alignment"
|
|
327
|
+
method_option :host, type: :boolean, aliases: %(-S),
|
|
328
|
+
desc: "Display only hostnames, one per newline (use options -uS (--user --host) for user@hostname)"
|
|
329
|
+
method_option :user, type: :boolean, aliases: %w(-u),
|
|
330
|
+
desc: "Include the ssh username in front of the hostname for easy SSH scripting"
|
|
331
|
+
method_option :account, type: :string, aliases: %w(-c),
|
|
332
|
+
required: true, default: '',
|
|
333
|
+
desc: "Find environment in this account"
|
|
334
|
+
method_option :environment, type: :string, aliases: %w(-e),
|
|
335
|
+
required: true, default: '',
|
|
336
|
+
desc: "Show servers in environment matching environment name"
|
|
337
|
+
method_option :all, type: :boolean, aliases: %(-A),
|
|
338
|
+
desc: "Show all servers (for compatibility only, this is the default for this command)"
|
|
339
|
+
method_option :app_master, type: :boolean,
|
|
340
|
+
desc: "Show only app master server"
|
|
341
|
+
method_option :app_servers, type: :boolean, aliases: %w(--app),
|
|
342
|
+
desc: "Show only application servers"
|
|
343
|
+
method_option :db_servers, type: :boolean, aliases: %w(--db),
|
|
344
|
+
desc: "Show only database servers"
|
|
345
|
+
method_option :db_master, type: :boolean,
|
|
346
|
+
desc: "Show only the master database server"
|
|
347
|
+
method_option :db_slaves, type: :boolean,
|
|
348
|
+
desc: "Show only the slave database servers"
|
|
349
|
+
method_option :utilities, type: :array, lazy_default: true, aliases: %w(--util),
|
|
350
|
+
desc: "Show only utility servers or only utility servers with the given names"
|
|
351
|
+
def servers
|
|
352
|
+
if options[:environment] == '' && options[:account] == ''
|
|
353
|
+
repo.fail_on_no_remotes!
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
environment = nil
|
|
357
|
+
ui.mute_if(options[:simple] || options[:host]) do
|
|
358
|
+
environment = fetch_environment(options[:environment], options[:account])
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
username = options[:user] && environment.username
|
|
362
|
+
|
|
363
|
+
servers = filter_servers(environment, options, default: {all: true})
|
|
364
|
+
|
|
365
|
+
if options[:host]
|
|
366
|
+
ui.print_hostnames(servers, username)
|
|
367
|
+
elsif options[:simple]
|
|
368
|
+
ui.print_simple_servers(servers, username)
|
|
369
|
+
else
|
|
370
|
+
ui.print_servers(servers, environment.hierarchy_name, username)
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
desc "rebuild [--environment ENVIRONMENT]", "Rebuild specified environment."
|
|
375
|
+
long_desc <<-DESC
|
|
376
|
+
Engine Yard's main configuration run occurs on all servers. Mainly used to fix
|
|
377
|
+
failed configuration of new or existing servers, or to update servers to latest
|
|
378
|
+
Engine Yard stack (e.g. to apply an Engine Yard supplied security
|
|
379
|
+
patch).
|
|
380
|
+
|
|
381
|
+
Note that uploaded recipes are also run after the main configuration run has
|
|
382
|
+
successfully completed.
|
|
383
|
+
DESC
|
|
384
|
+
|
|
385
|
+
method_option :environment, type: :string, aliases: %w(-e),
|
|
386
|
+
required: true, default: '',
|
|
387
|
+
desc: "Environment to rebuild"
|
|
388
|
+
method_option :account, type: :string, aliases: %w(-c),
|
|
389
|
+
required: true, default: '',
|
|
390
|
+
desc: "Name of the account in which the environment can be found"
|
|
391
|
+
def rebuild
|
|
392
|
+
environment = fetch_environment(options[:environment], options[:account])
|
|
393
|
+
ui.info "Updating instances on #{environment.hierarchy_name}"
|
|
394
|
+
environment.rebuild
|
|
395
|
+
end
|
|
396
|
+
map "update" => :rebuild
|
|
397
|
+
|
|
398
|
+
desc "rollback [--environment ENVIRONMENT]", "Rollback to the previous deploy."
|
|
399
|
+
long_desc <<-DESC
|
|
400
|
+
Uses code from previous deploy in the "/data/APP_NAME/releases" directory on
|
|
401
|
+
remote server(s) to restart application servers.
|
|
402
|
+
DESC
|
|
403
|
+
|
|
404
|
+
method_option :environment, type: :string, aliases: %w(-e),
|
|
405
|
+
required: true, default: '',
|
|
406
|
+
desc: "Environment in which to roll back the application"
|
|
407
|
+
method_option :app, type: :string, aliases: %w(-a),
|
|
408
|
+
required: true, default: '',
|
|
409
|
+
desc: "Name of the application to roll back"
|
|
410
|
+
method_option :account, type: :string, aliases: %w(-c),
|
|
411
|
+
required: true, default: '',
|
|
412
|
+
desc: "Name of the account in which the environment can be found"
|
|
413
|
+
method_option :verbose, type: :boolean, aliases: %w(-v),
|
|
414
|
+
desc: "Be verbose"
|
|
415
|
+
method_option :config, type: :hash, default: {}, aliases: %w(--extra-deploy-hook-options),
|
|
416
|
+
desc: "Hash made available in deploy hooks (in the 'config' hash), can also override some ey.yml settings."
|
|
417
|
+
def rollback
|
|
418
|
+
app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
|
|
419
|
+
env_config = config.environment_config(app_env.environment_name)
|
|
420
|
+
deploy_config = EY::DeployConfig.new(options, env_config, repo, ui)
|
|
421
|
+
|
|
422
|
+
ui.info "Rolling back #{app_env.hierarchy_name}"
|
|
423
|
+
|
|
424
|
+
runner = serverside_runner(app_env, deploy_config.verbose)
|
|
425
|
+
runner.rollback do |args|
|
|
426
|
+
args.config = {'deployed_by' => api.current_user.name, 'input_ref' => 'N/A'}.merge(deploy_config.extra_config || {})
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
if runner.call(ui.out, ui.err)
|
|
430
|
+
ui.info "Rollback complete"
|
|
431
|
+
else
|
|
432
|
+
raise EY::Error, "Rollback failed"
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
desc "ssh [COMMAND] [--all] [--environment ENVIRONMENT]", "Open an ssh session to the master app server, or run a command."
|
|
437
|
+
long_desc <<-DESC
|
|
438
|
+
If a command is supplied, it will be run, otherwise a session will be
|
|
439
|
+
opened. The bridge server (app master) is used for environments with multiple instances.
|
|
440
|
+
|
|
441
|
+
Option --all requires a command to be supplied and runs it on all servers or
|
|
442
|
+
pass --each to connect to each server one after another.
|
|
443
|
+
|
|
444
|
+
Note: this command is a bit picky about its ordering. To run a command with arguments on
|
|
445
|
+
all servers, like "rm -f /some/file", you need to order it like so:
|
|
446
|
+
|
|
447
|
+
$ #{banner_base} ssh "rm -f /some/file" -e my-environment --all
|
|
448
|
+
DESC
|
|
449
|
+
method_option :environment, type: :string, aliases: %w(-e),
|
|
450
|
+
required: true, default: '',
|
|
451
|
+
desc: "Environment to ssh into"
|
|
452
|
+
method_option :account, type: :string, aliases: %w(-c),
|
|
453
|
+
required: true, default: '',
|
|
454
|
+
desc: "Name of the account in which the environment can be found"
|
|
455
|
+
method_option :all, type: :boolean, aliases: %(-A),
|
|
456
|
+
desc: "Run command on all servers"
|
|
457
|
+
method_option :app_servers, type: :boolean,
|
|
458
|
+
desc: "Run command on all application servers"
|
|
459
|
+
method_option :db_servers, type: :boolean,
|
|
460
|
+
desc: "Run command on the database servers"
|
|
461
|
+
method_option :db_master, type: :boolean,
|
|
462
|
+
desc: "Run command on the master database server"
|
|
463
|
+
method_option :db_slaves, type: :boolean,
|
|
464
|
+
desc: "Run command on the slave database servers"
|
|
465
|
+
method_option :utilities, type: :array, lazy_default: true,
|
|
466
|
+
desc: "Run command on the utility servers with the given names. If no names are given, run on all utility servers."
|
|
467
|
+
method_option :shell, type: :string, default: 'bash', aliases: %w(-s),
|
|
468
|
+
desc: "Run command in a shell other than bash. Use --no-shell to run the command without a shell."
|
|
469
|
+
method_option :pty, type: :boolean, default: false, aliases: %w(-t),
|
|
470
|
+
desc: "If a command is given, run in a pty. Required for interactive commands like sudo."
|
|
471
|
+
method_option :bind_address, type: :string, aliases: %w(-L),
|
|
472
|
+
desc: "When a command is not given, pass -L to the ssh command."
|
|
473
|
+
method_option :each, type: :boolean, default: false,
|
|
474
|
+
desc: "If no command is given, connect to multiple servers each one after another, instead of exiting with an error."
|
|
475
|
+
|
|
476
|
+
def ssh(cmd=nil)
|
|
477
|
+
environment = fetch_environment(options[:environment], options[:account])
|
|
478
|
+
instances = filter_servers(environment, options, default: {app_master: true})
|
|
479
|
+
user = environment.username
|
|
480
|
+
ssh_opts = []
|
|
481
|
+
|
|
482
|
+
if cmd
|
|
483
|
+
if options[:shell]
|
|
484
|
+
cmd = Escape.shell_command([options[:shell],'-lc',cmd])
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
if options[:pty]
|
|
488
|
+
ssh_opts = ["-t"]
|
|
489
|
+
elsif cmd =~ /sudo/
|
|
490
|
+
ui.warn "sudo commands often need a tty to run correctly. Use -t option to spawn a tty."
|
|
491
|
+
end
|
|
492
|
+
else
|
|
493
|
+
if instances.size != 1 && options[:each] == false
|
|
494
|
+
raise NoCommandError.new
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
if options[:bind_address]
|
|
498
|
+
ssh_opts = ["-L", options[:bind_address]]
|
|
499
|
+
end
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
ssh_cmd = ["ssh"]
|
|
503
|
+
ssh_cmd += ssh_opts
|
|
504
|
+
|
|
505
|
+
trap(:INT) { abort "Aborting..." }
|
|
506
|
+
|
|
507
|
+
exits = []
|
|
508
|
+
instances.each do |instance|
|
|
509
|
+
host = instance.public_hostname
|
|
510
|
+
name = instance.name ? "#{instance.role} (#{instance.name})" : instance.role
|
|
511
|
+
ui.info "\nConnecting to #{name} #{host}..."
|
|
512
|
+
unless cmd
|
|
513
|
+
ui.info "Ctrl + C to abort"
|
|
514
|
+
sleep 1.3
|
|
515
|
+
end
|
|
516
|
+
sshcmd = Escape.shell_command((ssh_cmd + ["#{user}@#{host}"] + [cmd]).compact)
|
|
517
|
+
ui.debug "$ #{sshcmd}"
|
|
518
|
+
system sshcmd
|
|
519
|
+
exits << $?.exitstatus
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
exit exits.detect {|status| status != 0 } || 0
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
desc "console [--app APP] [--environment ENVIRONMENT] [--account ACCOUNT]", "Open a Rails console session to the master app server."
|
|
526
|
+
long_desc <<-DESC
|
|
527
|
+
Opens a Rails console session on app master.
|
|
528
|
+
DESC
|
|
529
|
+
method_option :environment, type: :string, aliases: %w(-e),
|
|
530
|
+
required: true, default: '',
|
|
531
|
+
desc: "Environment to console into"
|
|
532
|
+
method_option :app, type: :string, aliases: %w(-a),
|
|
533
|
+
required: true, default: '',
|
|
534
|
+
desc: "Name of the application"
|
|
535
|
+
method_option :account, type: :string, aliases: %w(-c),
|
|
536
|
+
required: true, default: '',
|
|
537
|
+
desc: "Name of the account in which the environment can be found"
|
|
538
|
+
|
|
539
|
+
def console
|
|
540
|
+
app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
|
|
541
|
+
instances = filter_servers(app_env.environment, options, default: {app_master: true})
|
|
542
|
+
user = app_env.environment.username
|
|
543
|
+
cmd = "cd /data/#{app_env.app.name}/current && current_user=#{api.current_user.name} bundle exec rails console"
|
|
544
|
+
cmd = Escape.shell_command(['bash','-lc',cmd])
|
|
545
|
+
|
|
546
|
+
ssh_cmd = ["ssh"]
|
|
547
|
+
ssh_cmd += ["-t"]
|
|
548
|
+
|
|
549
|
+
trap(:INT) { abort "Aborting..." }
|
|
550
|
+
|
|
551
|
+
exits = []
|
|
552
|
+
instances.each do |instance|
|
|
553
|
+
host = instance.public_hostname
|
|
554
|
+
name = instance.name ? "#{instance.role} (#{instance.name})" : instance.role
|
|
555
|
+
ui.info "\nConnecting to #{name} #{host}..."
|
|
556
|
+
unless cmd
|
|
557
|
+
ui.info "Ctrl + C to abort"
|
|
558
|
+
sleep 1.3
|
|
559
|
+
end
|
|
560
|
+
sshcmd = Escape.shell_command((ssh_cmd + ["#{user}@#{host}"] + [cmd]).compact)
|
|
561
|
+
ui.debug "$ #{sshcmd}"
|
|
562
|
+
system sshcmd
|
|
563
|
+
exits << $?.exitstatus
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
exit exits.detect {|status| status != 0 } || 0
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
desc "scp [FROM_PATH] [TO_PATH] [--all] [--environment ENVIRONMENT]", "scp a file to/from multiple servers in an environment"
|
|
570
|
+
long_desc <<-DESC
|
|
571
|
+
Use the system `scp` command to copy files to some or all of the servers.
|
|
572
|
+
|
|
573
|
+
If `HOST:` is found in the FROM_PATH or TO_PATH, the server name will be
|
|
574
|
+
substituted in place of `HOST:` when scp is run. This allows you to scp in
|
|
575
|
+
either direction by putting `HOST:` in the FROM_PATH or TO_PATH, as follows:
|
|
576
|
+
|
|
577
|
+
$ #{banner_base} scp example.json HOST:/data/app_name/current/config/ -e env --app-servers
|
|
578
|
+
|
|
579
|
+
$ #{banner_base} scp HOST:/data/app_name/current/config/example.json ./ -e env --app-servers
|
|
580
|
+
|
|
581
|
+
If `HOST:` is not specified, TO_PATH will be used as the remote path.
|
|
582
|
+
Be sure to escape shell words so they don't expand locally (e.g. '~').
|
|
583
|
+
|
|
584
|
+
Note: this command is a bit picky about its ordering. FROM_PATH TO_PATH
|
|
585
|
+
must follow immediately after `ey scp` with no flags in between.
|
|
586
|
+
DESC
|
|
587
|
+
method_option :environment, :type => :string, :aliases => %w(-e),
|
|
588
|
+
:required => true, :default => '',
|
|
589
|
+
:desc => "Name of the destination environment"
|
|
590
|
+
method_option :account, :type => :string, :aliases => %w(-c),
|
|
591
|
+
:required => true, :default => '',
|
|
592
|
+
:desc => "Name of the account in which the environment can be found"
|
|
593
|
+
method_option :all, :type => :boolean, :aliases => %(-A),
|
|
594
|
+
:desc => "scp to all servers"
|
|
595
|
+
method_option :app_servers, :type => :boolean,
|
|
596
|
+
:desc => "scp to all application servers"
|
|
597
|
+
method_option :db_servers, :type => :boolean,
|
|
598
|
+
:desc => "scp to database servers"
|
|
599
|
+
method_option :db_master, :type => :boolean,
|
|
600
|
+
:desc => "scp to the master database server"
|
|
601
|
+
method_option :db_slaves, :type => :boolean,
|
|
602
|
+
:desc => "scp to the slave database servers"
|
|
603
|
+
method_option :utilities, :type => :array, :lazy_default => true,
|
|
604
|
+
:desc => "scp to all utility servers or only those with the given names"
|
|
605
|
+
|
|
606
|
+
def scp(from_path, to_path)
|
|
607
|
+
environment = fetch_environment(options[:environment], options[:account])
|
|
608
|
+
instances = filter_servers(environment, options, default: {app_master: true})
|
|
609
|
+
user = environment.username
|
|
610
|
+
|
|
611
|
+
ui.info "Copying '#{from_path}' to '#{to_path}' on #{instances.count} server#{instances.count == 1 ? '' : 's'} serially..."
|
|
612
|
+
|
|
613
|
+
# default to `scp FROM_PATH HOST:TO_PATH`
|
|
614
|
+
unless [from_path, to_path].detect { |path| path =~ /HOST:/ }
|
|
615
|
+
to_path = "HOST:#{to_path}"
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
exits = []
|
|
619
|
+
instances.each do |instance|
|
|
620
|
+
host = instance.public_hostname
|
|
621
|
+
authority = "#{user}@#{host}:"
|
|
622
|
+
|
|
623
|
+
name = instance.name ? "#{instance.role} (#{instance.name})" : instance.role
|
|
624
|
+
ui.info "# #{name} #{host}"
|
|
625
|
+
|
|
626
|
+
from = from_path.sub(/^HOST:/, authority)
|
|
627
|
+
to = to_path.sub(/^HOST:/, authority)
|
|
628
|
+
|
|
629
|
+
cmd = Escape.shell_command(["scp", from, to])
|
|
630
|
+
ui.debug "$ #{cmd}"
|
|
631
|
+
system cmd
|
|
632
|
+
exits << $?.exitstatus
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
exit exits.detect {|status| status != 0 } || 0
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
no_tasks do
|
|
639
|
+
OPT_TO_ROLES = {
|
|
640
|
+
all: %w[all],
|
|
641
|
+
app_master: %w[solo app_master],
|
|
642
|
+
app_servers: %w[solo app app_master],
|
|
643
|
+
db_servers: %w[solo db_master db_slave],
|
|
644
|
+
db_master: %w[solo db_master],
|
|
645
|
+
db_slaves: %w[db_slave],
|
|
646
|
+
utilities: %w[util],
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
def filter_servers(environment, cli_opts, filter_opts)
|
|
650
|
+
if (cli_opts.keys.map(&:to_sym) & OPT_TO_ROLES.keys).any?
|
|
651
|
+
options = cli_opts.dup
|
|
652
|
+
else
|
|
653
|
+
options = filter_opts[:default].dup
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
options.keep_if {|k,v| OPT_TO_ROLES.has_key?(k.to_sym) }
|
|
657
|
+
|
|
658
|
+
if options[:all]
|
|
659
|
+
instances = environment.instances
|
|
660
|
+
else
|
|
661
|
+
roles = {}
|
|
662
|
+
options.each do |cli_opt,cli_val|
|
|
663
|
+
if cli_val && OPT_TO_ROLES.has_key?(cli_opt.to_sym)
|
|
664
|
+
OPT_TO_ROLES[cli_opt.to_sym].each do |role|
|
|
665
|
+
roles[role] = cli_val # val is true or an array of strings
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
end
|
|
669
|
+
instances = environment.select_instances(roles)
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
if instances.empty?
|
|
673
|
+
raise NoInstancesError.new(environment.name)
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
return instances
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
desc "logs [--environment ENVIRONMENT]", "Retrieve the latest logs for an environment."
|
|
681
|
+
long_desc <<-DESC
|
|
682
|
+
Displays Engine Yard configuration logs for all servers in the environment. If
|
|
683
|
+
recipes were uploaded to the environment & run, their logs will also be
|
|
684
|
+
displayed beneath the main configuration logs.
|
|
685
|
+
DESC
|
|
686
|
+
method_option :environment, type: :string, aliases: %w(-e),
|
|
687
|
+
required: true, default: '',
|
|
688
|
+
desc: "Environment with the interesting logs"
|
|
689
|
+
method_option :account, type: :string, aliases: %w(-c),
|
|
690
|
+
required: true, default: '',
|
|
691
|
+
desc: "Name of the account in which the environment can be found"
|
|
692
|
+
def logs
|
|
693
|
+
environment = fetch_environment(options[:environment], options[:account])
|
|
694
|
+
environment.logs.each do |log|
|
|
695
|
+
ui.say "Instance: #{log.instance_name}"
|
|
696
|
+
|
|
697
|
+
if log.main
|
|
698
|
+
ui.say "Main logs for #{environment.name}:", :green
|
|
699
|
+
ui.say log.main
|
|
700
|
+
end
|
|
701
|
+
|
|
702
|
+
if log.custom
|
|
703
|
+
ui.say "Custom logs for #{environment.name}:", :green
|
|
704
|
+
ui.say log.custom
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
desc "recipes", "Commands related to chef recipes."
|
|
710
|
+
subcommand "recipes", EY::CLI::Recipes
|
|
711
|
+
|
|
712
|
+
desc "web", "Commands related to maintenance pages."
|
|
713
|
+
subcommand "web", EY::CLI::Web
|
|
714
|
+
|
|
715
|
+
desc "version", "Print version number."
|
|
716
|
+
def version
|
|
717
|
+
ui.say %{engineyard version #{EY::VERSION}}
|
|
718
|
+
end
|
|
719
|
+
map ["-v", "--version"] => :version
|
|
720
|
+
|
|
721
|
+
desc "help [COMMAND]", "Describe all commands or one specific command."
|
|
722
|
+
def help(*cmds)
|
|
723
|
+
if cmds.empty?
|
|
724
|
+
base = self.class.send(:banner_base)
|
|
725
|
+
list = self.class.printable_tasks
|
|
726
|
+
|
|
727
|
+
ui.say "Usage:"
|
|
728
|
+
ui.say " #{base} [--help] [--version] COMMAND [ARGS]"
|
|
729
|
+
ui.say
|
|
730
|
+
|
|
731
|
+
ui.say "Deploy commands:"
|
|
732
|
+
deploy_cmds = %w(deploy environments logs rebuild rollback status)
|
|
733
|
+
deploy_cmds.map! do |name|
|
|
734
|
+
list.find{|task| task[0] =~ /^#{base} #{name}/ }
|
|
735
|
+
end
|
|
736
|
+
list -= deploy_cmds
|
|
737
|
+
|
|
738
|
+
ui.print_help(deploy_cmds)
|
|
739
|
+
ui.say
|
|
740
|
+
|
|
741
|
+
self.class.subcommands.each do |name|
|
|
742
|
+
klass = self.class.subcommand_class_for(name)
|
|
743
|
+
list.reject!{|cmd| cmd[0] =~ /^#{base} #{name}/}
|
|
744
|
+
ui.say "#{name.capitalize} commands:"
|
|
745
|
+
tasks = klass.printable_tasks.reject{|t| t[0] =~ /help$/ }
|
|
746
|
+
ui.print_help(tasks)
|
|
747
|
+
ui.say
|
|
748
|
+
end
|
|
749
|
+
|
|
750
|
+
%w(help version).each{|n| list.reject!{|c| c[0] =~ /^#{base} #{n}/ } }
|
|
751
|
+
if list.any?
|
|
752
|
+
ui.say "Other commands:"
|
|
753
|
+
ui.print_help(list)
|
|
754
|
+
ui.say
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
self.class.send(:class_options_help, shell)
|
|
758
|
+
ui.say "See '#{base} help COMMAND' for more information on a specific command."
|
|
759
|
+
elsif klass = self.class.subcommand_class_for(cmds.first)
|
|
760
|
+
klass.new.help(*cmds[1..-1])
|
|
761
|
+
else
|
|
762
|
+
super
|
|
763
|
+
end
|
|
764
|
+
end
|
|
765
|
+
|
|
766
|
+
desc "launch [--app APP] [--environment ENVIRONMENT] [--account ACCOUNT]", "Open application in browser."
|
|
767
|
+
method_option :environment, type: :string, aliases: %w(-e),
|
|
768
|
+
required: true, default: '',
|
|
769
|
+
desc: "Environment where the application is deployed"
|
|
770
|
+
method_option :app, type: :string, aliases: %w(-a),
|
|
771
|
+
required: true, default: '',
|
|
772
|
+
desc: "Name of the application"
|
|
773
|
+
method_option :account, type: :string, aliases: %w(-c),
|
|
774
|
+
required: true, default: '',
|
|
775
|
+
desc: "Name of the account in which the application can be found"
|
|
776
|
+
def launch
|
|
777
|
+
app_env = fetch_app_environment(options[:app], options[:environment], options[:account])
|
|
778
|
+
Launchy.open(app_env.uri)
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
desc "whoami", "Who am I logged in as?"
|
|
782
|
+
def whoami
|
|
783
|
+
current_user = api.current_user
|
|
784
|
+
ui.say "#{current_user.name} (#{current_user.email})"
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
desc "login", "Log in and verify access to Engine Yard Cloud."
|
|
788
|
+
long_desc <<-DESC
|
|
789
|
+
You may run this command to log in to EY Cloud without performing
|
|
790
|
+
any other action.
|
|
791
|
+
|
|
792
|
+
Once you are logged in, a file will be stored at ~/.eyrc with your
|
|
793
|
+
API token. You may override the location of this file using the
|
|
794
|
+
$EYRC environment variable.
|
|
795
|
+
|
|
796
|
+
Instead of logging in, you may specify a token on the command line
|
|
797
|
+
with --api-token or using the $ENGINEYARD_API_TOKEN environment
|
|
798
|
+
variable.
|
|
799
|
+
DESC
|
|
800
|
+
def login
|
|
801
|
+
whoami
|
|
802
|
+
end
|
|
803
|
+
|
|
804
|
+
desc "logout", "Remove the current API key from ~/.eyrc or env variable $EYRC"
|
|
805
|
+
def logout
|
|
806
|
+
eyrc = EYRC.load
|
|
807
|
+
if eyrc.delete_api_token
|
|
808
|
+
ui.info "API token removed: #{eyrc.path}"
|
|
809
|
+
ui.info "Run any other command to login again."
|
|
810
|
+
else
|
|
811
|
+
ui.info "Already logged out. Run any other command to login again."
|
|
812
|
+
end
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
end # CLI
|
|
816
|
+
end # EY
|