vmc_virgo 0.0.1.beta

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.
data/lib/cli/runner.rb ADDED
@@ -0,0 +1,521 @@
1
+
2
+ require 'optparse'
3
+
4
+ require File.dirname(__FILE__) + '/usage'
5
+
6
+ class VMC::Cli::Runner
7
+
8
+ attr_reader :namespace
9
+ attr_reader :action
10
+ attr_reader :args
11
+ attr_reader :options
12
+
13
+ def self.run(args)
14
+ new(args).run
15
+ end
16
+
17
+ def initialize(args=[])
18
+ @args = args
19
+ @options = { :colorize => true }
20
+ @exit_status = true
21
+ end
22
+
23
+ # Collect all the available options for all commands
24
+ # Some duplicates exists to capture all scenarios
25
+ def parse_options!
26
+ opts_parser = OptionParser.new do |opts|
27
+ opts.banner = "\nAvailable options:\n\n"
28
+
29
+ opts.on('--email EMAIL') { |email| @options[:email] = email }
30
+ opts.on('--user EMAIL') { |email| @options[:email] = email }
31
+ opts.on('--passwd PASS') { |pass| @options[:password] = pass }
32
+ opts.on('--pass PASS') { |pass| @options[:password] = pass }
33
+ opts.on('--password PASS') { |pass| @options[:password] = pass }
34
+ opts.on('--token-file TOKEN_FILE') { |token_file| @options[:token_file] = token_file }
35
+ opts.on('--app NAME') { |name| @options[:name] = name }
36
+ opts.on('--name NAME') { |name| @options[:name] = name }
37
+ opts.on('--bind BIND') { |bind| @options[:bind] = bind }
38
+ opts.on('--instance INST') { |inst| @options[:instance] = inst }
39
+ opts.on('--instances INST') { |inst| @options[:instances] = inst }
40
+ opts.on('--url URL') { |url| @options[:url] = url }
41
+ opts.on('--mem MEM') { |mem| @options[:mem] = mem }
42
+ opts.on('--path PATH') { |path| @options[:path] = path }
43
+ opts.on('--no-start') { @options[:nostart] = true }
44
+ opts.on('--nostart') { @options[:nostart] = true }
45
+ opts.on('--force') { @options[:force] = true }
46
+ opts.on('--all') { @options[:all] = true }
47
+
48
+ # generic tracing and debugging
49
+ opts.on('-t [TKEY]') { |tkey| @options[:trace] = tkey || true }
50
+ opts.on('--trace [TKEY]') { |tkey| @options[:trace] = tkey || true }
51
+
52
+ # start application in debug mode
53
+ opts.on('-d [MODE]') { |mode| @options[:debug] = mode || "run" }
54
+ opts.on('--debug [MODE]') { |mode| @options[:debug] = mode || "run" }
55
+
56
+ # override manifest file
57
+ opts.on('-m FILE') { |file| @options[:manifest] = file }
58
+ opts.on('--manifest FILE') { |file| @options[:manifest] = file }
59
+
60
+ opts.on('-q', '--quiet') { @options[:quiet] = true }
61
+
62
+ # Don't use builtin zip
63
+ opts.on('--no-zip') { @options[:nozip] = true }
64
+ opts.on('--nozip') { @options[:nozip] = true }
65
+
66
+ opts.on('--no-resources') { @options[:noresources] = true }
67
+ opts.on('--noresources') { @options[:noresources] = true }
68
+
69
+ opts.on('--no-color') { @options[:colorize] = false }
70
+ opts.on('--verbose') { @options[:verbose] = true }
71
+
72
+ opts.on('-n','--no-prompt') { @options[:noprompts] = true }
73
+ opts.on('--noprompt') { @options[:noprompts] = true }
74
+ opts.on('--non-interactive') { @options[:noprompts] = true }
75
+
76
+ opts.on('--prefix') { @options[:prefixlogs] = true }
77
+ opts.on('--prefix-logs') { @options[:prefixlogs] = true }
78
+ opts.on('--prefixlogs') { @options[:prefixlogs] = true }
79
+
80
+ opts.on('--json') { @options[:json] = true }
81
+
82
+ opts.on('-v', '--version') { set_cmd(:misc, :version) }
83
+ opts.on('-h', '--help') { puts "#{command_usage}\n"; exit }
84
+
85
+ opts.on('--port PORT') { |port| @options[:port] = port }
86
+
87
+ opts.on('--runtime RUNTIME') { |rt| @options[:runtime] = rt }
88
+
89
+ # deprecated
90
+ opts.on('--exec EXEC') { |exec| @options[:exec] = exec }
91
+ opts.on('--noframework') { @options[:noframework] = true }
92
+ opts.on('--canary') { @options[:canary] = true }
93
+
94
+ # Proxying for another user, requires admin privileges
95
+ opts.on('-u PROXY') { |proxy| @options[:proxy] = proxy }
96
+
97
+ opts.on_tail('--options') { puts "#{opts}\n"; exit }
98
+ end
99
+ instances_delta_arg = check_instances_delta!
100
+ @args = opts_parser.parse!(@args)
101
+ @args.concat instances_delta_arg
102
+ convert_options!
103
+ self
104
+ end
105
+
106
+ def check_instances_delta!
107
+ return unless @args
108
+ instance_args = @args.select { |arg| /^[-]\d+$/ =~ arg } || []
109
+ @args.delete_if { |arg| instance_args.include? arg}
110
+ instance_args
111
+ end
112
+
113
+ def display_help
114
+ puts command_usage
115
+ exit
116
+ end
117
+
118
+ def convert_options!
119
+ # make sure certain options are valid and in correct form.
120
+ @options[:instances] = Integer(@options[:instances]) if @options[:instances]
121
+ end
122
+
123
+ def set_cmd(namespace, action, args_range=0)
124
+ return if @help_only
125
+ unless args_range == "*" || args_range.is_a?(Range)
126
+ args_range = (args_range.to_i..args_range.to_i)
127
+ end
128
+
129
+ if args_range == "*" || args_range.include?(@args.size)
130
+ @namespace = namespace
131
+ @action = action
132
+ else
133
+ @exit_status = false
134
+ if @args.size > args_range.last
135
+ usage_error("Too many arguments for [#{action}]: %s" % [ @args[args_range.last..-1].map{|a| "'#{a}'"}.join(', ') ])
136
+ else
137
+ usage_error("Not enough arguments for [#{action}]")
138
+ end
139
+ end
140
+ end
141
+
142
+ def parse_command!
143
+ # just return if already set, happends with -v, -h
144
+ return if @namespace && @action
145
+
146
+ verb = @args.shift
147
+ case verb
148
+
149
+ when 'version'
150
+ usage('vmc version')
151
+ set_cmd(:misc, :version)
152
+
153
+ when 'target'
154
+ usage('vmc target [url] [--url]')
155
+ if @args.size == 1
156
+ set_cmd(:misc, :set_target, 1)
157
+ else
158
+ set_cmd(:misc, :target)
159
+ end
160
+
161
+ when 'targets'
162
+ usage('vmc targets')
163
+ set_cmd(:misc, :targets)
164
+
165
+ when 'tokens'
166
+ usage('vmc tokens')
167
+ set_cmd(:misc, :tokens)
168
+
169
+ when 'info'
170
+ usage('vmc info')
171
+ set_cmd(:misc, :info)
172
+
173
+ when 'runtimes'
174
+ usage('vmc runtimes')
175
+ set_cmd(:misc, :runtimes)
176
+
177
+ when 'frameworks'
178
+ usage('vmc frameworks')
179
+ set_cmd(:misc, :frameworks)
180
+
181
+ when 'user'
182
+ usage('vmc user')
183
+ set_cmd(:user, :info)
184
+
185
+ when 'login'
186
+ usage('vmc login [email] [--email EMAIL] [--passwd PASS]')
187
+ if @args.size == 1
188
+ set_cmd(:user, :login, 1)
189
+ else
190
+ set_cmd(:user, :login)
191
+ end
192
+
193
+ when 'logout'
194
+ usage('vmc logout')
195
+ set_cmd(:user, :logout)
196
+
197
+ when 'passwd'
198
+ usage('vmc passwd')
199
+ if @args.size == 1
200
+ set_cmd(:user, :change_password, 1)
201
+ else
202
+ set_cmd(:user, :change_password)
203
+ end
204
+
205
+ when 'add-user', 'add_user', 'create_user', 'create-user', 'register'
206
+ usage('vmc add-user [user] [--email EMAIL] [--passwd PASS]')
207
+ if @args.size == 1
208
+ set_cmd(:admin, :add_user, 1)
209
+ else
210
+ set_cmd(:admin, :add_user)
211
+ end
212
+
213
+ when 'delete-user', 'delete_user', 'unregister'
214
+ usage('vmc delete-user <user>')
215
+ set_cmd(:admin, :delete_user, 1)
216
+
217
+ when 'users'
218
+ usage('vmc users')
219
+ set_cmd(:admin, :users)
220
+
221
+ when 'apps'
222
+ usage('vmc apps')
223
+ set_cmd(:apps, :apps)
224
+
225
+ when 'list'
226
+ usage('vmc list')
227
+ set_cmd(:apps, :list)
228
+
229
+ when 'start'
230
+ usage('vmc start <appname>')
231
+ set_cmd(:apps, :start, @args.size == 1 ? 1 : 0)
232
+
233
+ when 'stop'
234
+ usage('vmc stop <appname>')
235
+ set_cmd(:apps, :stop, @args.size == 1 ? 1 : 0)
236
+
237
+ when 'restart'
238
+ usage('vmc restart <appname>')
239
+ set_cmd(:apps, :restart, @args.size == 1 ? 1 : 0)
240
+
241
+ when 'mem'
242
+ usage('vmc mem <appname> [memsize]')
243
+ if @args.size == 2
244
+ set_cmd(:apps, :mem, 2)
245
+ else
246
+ set_cmd(:apps, :mem, 1)
247
+ end
248
+
249
+ when 'stats'
250
+ usage('vmc stats <appname>')
251
+ set_cmd(:apps, :stats, @args.size == 1 ? 1 : 0)
252
+
253
+ when 'map'
254
+ usage('vmc map <appname> <url>')
255
+ set_cmd(:apps, :map, 2)
256
+
257
+ when 'unmap'
258
+ usage('vmc unmap <appname> <url>')
259
+ set_cmd(:apps, :unmap, 2)
260
+
261
+ when 'delete'
262
+ usage('vmc delete <appname>')
263
+ if @options[:all] && @args.size == 0
264
+ set_cmd(:apps, :delete)
265
+ else
266
+ set_cmd(:apps, :delete, 1)
267
+ end
268
+
269
+ when 'files'
270
+ usage('vmc files <appname> [path] [--instance N] [--all] [--prefix]')
271
+ if @args.size == 1
272
+ set_cmd(:apps, :files, 1)
273
+ else
274
+ set_cmd(:apps, :files, 2)
275
+ end
276
+
277
+ when 'logs'
278
+ usage('vmc logs <appname> [--instance N] [--all] [--prefix]')
279
+ set_cmd(:apps, :logs, 1)
280
+
281
+ when 'instances', 'scale'
282
+ if @args.size > 1
283
+ usage('vmc instances <appname> <num|delta>')
284
+ set_cmd(:apps, :instances, 2)
285
+ else
286
+ usage('vmc instances <appname>')
287
+ set_cmd(:apps, :instances, @args.size == 1 ? 1 : 0)
288
+ end
289
+
290
+ when 'crashes'
291
+ usage('vmc crashes <appname>')
292
+ set_cmd(:apps, :crashes, 1)
293
+
294
+ when 'crashlogs'
295
+ usage('vmc crashlogs <appname>')
296
+ set_cmd(:apps, :crashlogs, 1)
297
+
298
+ when 'push'
299
+ usage('vmc push [appname] [--path PATH] [--url URL] [--instances N] [--mem] [--runtime RUNTIME] [--no-start]')
300
+ if @args.size == 1
301
+ set_cmd(:apps, :push, 1)
302
+ else
303
+ set_cmd(:apps, :push, 0)
304
+ end
305
+
306
+ when 'update'
307
+ usage('vmc update <appname> [--path PATH]')
308
+ set_cmd(:apps, :update, @args.size == 1 ? 1 : 0)
309
+
310
+ when 'services'
311
+ usage('vmc services')
312
+ set_cmd(:services, :services)
313
+
314
+ when 'env'
315
+ usage('vmc env <appname>')
316
+ set_cmd(:apps, :environment, 1)
317
+
318
+ when 'env-add'
319
+ usage('vmc env-add <appname> <variable[=]value>')
320
+ if @args.size == 2
321
+ set_cmd(:apps, :environment_add, 2)
322
+ elsif @args.size == 3
323
+ set_cmd(:apps, :environment_add, 3)
324
+ end
325
+
326
+ when 'env-del'
327
+ usage('vmc env-del <appname> <variable>')
328
+ set_cmd(:apps, :environment_del, 2)
329
+
330
+ when 'create-service', 'create_service'
331
+ usage('vmc create-service [service] [servicename] [appname] [--name servicename] [--bind appname]')
332
+ set_cmd(:services, :create_service) if @args.size == 0
333
+ set_cmd(:services, :create_service, 1) if @args.size == 1
334
+ set_cmd(:services, :create_service, 2) if @args.size == 2
335
+ set_cmd(:services, :create_service, 3) if @args.size == 3
336
+
337
+ when 'delete-service', 'delete_service'
338
+ usage('vmc delete-service <service>')
339
+ if @args.size == 1
340
+ set_cmd(:services, :delete_service, 1)
341
+ else
342
+ set_cmd(:services, :delete_service)
343
+ end
344
+
345
+ when 'bind-service', 'bind_service'
346
+ usage('vmc bind-service <servicename> <appname>')
347
+ set_cmd(:services, :bind_service, 2)
348
+
349
+ when 'unbind-service', 'unbind_service'
350
+ usage('vmc unbind-service <servicename> <appname>')
351
+ set_cmd(:services, :unbind_service, 2)
352
+
353
+ when 'clone-services'
354
+ usage('vmc clone-services <src-app> <dest-app>')
355
+ set_cmd(:services, :clone_services, 2)
356
+
357
+ when 'aliases'
358
+ usage('vmc aliases')
359
+ set_cmd(:misc, :aliases)
360
+
361
+ when 'alias'
362
+ usage('vmc alias <alias[=]command>')
363
+ if @args.size == 1
364
+ set_cmd(:misc, :alias, 1)
365
+ elsif @args.size == 2
366
+ set_cmd(:misc, :alias, 2)
367
+ end
368
+
369
+ when 'unalias'
370
+ usage('vmc unalias <alias>')
371
+ set_cmd(:misc, :unalias, 1)
372
+
373
+ when 'tunnel'
374
+ usage('vmc tunnel [servicename] [clientcmd] [--port port]')
375
+ set_cmd(:services, :tunnel, 0) if @args.size == 0
376
+ set_cmd(:services, :tunnel, 1) if @args.size == 1
377
+ set_cmd(:services, :tunnel, 2) if @args.size == 2
378
+
379
+ when 'rails-console'
380
+ usage('vmc rails-console <appname>')
381
+ set_cmd(:apps, :console, 1)
382
+
383
+ when 'help'
384
+ display_help if @args.size == 0
385
+ @help_only = true
386
+ parse_command!
387
+
388
+ when 'usage'
389
+ display basic_usage
390
+ exit(true)
391
+
392
+ when 'options'
393
+ # Simulate --options
394
+ @args = @args.unshift('--options')
395
+ parse_options!
396
+
397
+ when 'manifest'
398
+ usage('vmc manifest')
399
+ set_cmd(:manifest, :edit)
400
+
401
+ when 'extend-manifest'
402
+ usage('vmc extend-manifest')
403
+ set_cmd(:manifest, :extend, 1)
404
+
405
+ else
406
+ if verb
407
+ display "vmc: Unknown command [#{verb}]"
408
+ display basic_usage
409
+ exit(false)
410
+ end
411
+ end
412
+ end
413
+
414
+ def process_aliases!
415
+ return if @args.empty?
416
+ aliases = VMC::Cli::Config.aliases
417
+ aliases.each_pair do |k,v|
418
+ if @args[0] == k
419
+ display "[#{@args[0]} aliased to #{aliases.invert[key]}]" if @options[:verbose]
420
+ @args[0] = v
421
+ break;
422
+ end
423
+ end
424
+ end
425
+
426
+ def usage(msg = nil)
427
+ @usage = msg if msg
428
+ @usage
429
+ end
430
+
431
+ def usage_error(msg = nil)
432
+ @usage_error = msg if msg
433
+ @usage_error
434
+ end
435
+
436
+ def run
437
+
438
+ trap('TERM') { print "\nTerminated\n"; exit(false)}
439
+
440
+ parse_options!
441
+
442
+ @options[:colorize] = false unless STDOUT.tty?
443
+
444
+ VMC::Cli::Config.colorize = @options.delete(:colorize)
445
+ VMC::Cli::Config.nozip = @options.delete(:nozip)
446
+ VMC::Cli::Config.trace = @options.delete(:trace)
447
+ VMC::Cli::Config.output ||= STDOUT unless @options[:quiet]
448
+
449
+ process_aliases!
450
+ parse_command!
451
+
452
+ if @namespace && @action
453
+ cmd = VMC::Cli::Command.const_get(@namespace.to_s.capitalize)
454
+ cmd.new(@options).send(@action, *@args.collect(&:dup))
455
+ elsif @help_only || @usage
456
+ display_usage
457
+ else
458
+ display basic_usage
459
+ exit(false)
460
+ end
461
+
462
+ rescue OptionParser::InvalidOption => e
463
+ puts(e.message.red)
464
+ puts("\n")
465
+ puts(basic_usage)
466
+ @exit_status = false
467
+ rescue OptionParser::AmbiguousOption => e
468
+ puts(e.message.red)
469
+ puts("\n")
470
+ puts(basic_usage)
471
+ @exit_status = false
472
+ rescue VMC::Client::AuthError => e
473
+ if VMC::Cli::Config.auth_token.nil?
474
+ puts "Login Required".red
475
+ else
476
+ puts "Not Authorized".red
477
+ end
478
+ @exit_status = false
479
+ rescue VMC::Client::TargetError, VMC::Client::NotFound, VMC::Client::BadTarget => e
480
+ puts e.message.red
481
+ puts e.backtrace
482
+ @exit_status = false
483
+ rescue VMC::Client::HTTPException => e
484
+ puts e.message.red
485
+ @exit_status = false
486
+ rescue VMC::Cli::GracefulExit => e
487
+ # Redirected commands end up generating this exception (kind of goto)
488
+ rescue VMC::Cli::CliExit => e
489
+ puts e.message.red
490
+ @exit_status = false
491
+ rescue VMC::Cli::CliError => e
492
+ say("Error #{e.error_code}: #{e.message}".red)
493
+ @exit_status = false
494
+ rescue SystemExit => e
495
+ @exit_status = e.success?
496
+ rescue SyntaxError => e
497
+ puts e.message.red
498
+ puts e.backtrace
499
+ @exit_status = false
500
+ rescue Interrupt => e
501
+ say("\nInterrupted".red)
502
+ @exit_status = false
503
+ rescue Exception => e
504
+ puts e.message.red
505
+ puts e.backtrace
506
+ @exit_status = false
507
+ ensure
508
+ say("\n")
509
+ @exit_status == true if @exit_status.nil?
510
+ if @options[:verbose]
511
+ if @exit_status
512
+ puts "[#{@namespace}:#{@action}] SUCCEEDED".green
513
+ else
514
+ puts "[#{@namespace}:#{@action}] FAILED".red
515
+ end
516
+ say("\n")
517
+ end
518
+ exit(@exit_status)
519
+ end
520
+
521
+ end
@@ -0,0 +1,84 @@
1
+
2
+ module VMC::Cli
3
+ module ServicesHelper
4
+ def display_system_services(services=nil)
5
+ services ||= client.services_info
6
+
7
+ display "\n============== System Services ==============\n\n"
8
+
9
+ return display "No system services available" if services.empty?
10
+
11
+ displayed_services = []
12
+ services.each do |service_type, value|
13
+ value.each do |vendor, version|
14
+ version.each do |version_str, service|
15
+ displayed_services << [ vendor, version_str, service[:description] ]
16
+ end
17
+ end
18
+ end
19
+ displayed_services.sort! { |a, b| a.first.to_s <=> b.first.to_s}
20
+
21
+ services_table = table do |t|
22
+ t.headings = 'Service', 'Version', 'Description'
23
+ displayed_services.each { |s| t << s }
24
+ end
25
+ display services_table
26
+ end
27
+
28
+ def display_provisioned_services(services=nil)
29
+ services ||= client.services
30
+ display "\n=========== Provisioned Services ============\n\n"
31
+ display_provisioned_services_table(services)
32
+ end
33
+
34
+ def display_provisioned_services_table(services)
35
+ return unless services && !services.empty?
36
+ services_table = table do |t|
37
+ t.headings = 'Name', 'Service'
38
+ services.each do |service|
39
+ t << [ service[:name], service[:vendor] ]
40
+ end
41
+ end
42
+ display services_table
43
+ end
44
+
45
+ def create_service_banner(service, name, display_name=false)
46
+ sn = " [#{name}]" if display_name
47
+ display "Creating Service#{sn}: ", false
48
+ client.create_service(service, name)
49
+ display 'OK'.green
50
+ end
51
+
52
+ def bind_service_banner(service, appname, check_restart=true)
53
+ display "Binding Service [#{service}]: ", false
54
+ client.bind_service(service, appname)
55
+ display 'OK'.green
56
+ check_app_for_restart(appname) if check_restart
57
+ end
58
+
59
+ def unbind_service_banner(service, appname, check_restart=true)
60
+ display "Unbinding Service [#{service}]: ", false
61
+ client.unbind_service(service, appname)
62
+ display 'OK'.green
63
+ check_app_for_restart(appname) if check_restart
64
+ end
65
+
66
+ def delete_service_banner(service)
67
+ display "Deleting service [#{service}]: ", false
68
+ client.delete_service(service)
69
+ display 'OK'.green
70
+ end
71
+
72
+ def random_service_name(service)
73
+ r = "%04x" % [rand(0x0100000)]
74
+ "#{service.to_s}-#{r}"
75
+ end
76
+
77
+ def check_app_for_restart(appname)
78
+ app = client.app_info(appname)
79
+ cmd = VMC::Cli::Command::Apps.new(@options)
80
+ cmd.restart(appname) if app[:state] == 'STARTED'
81
+ end
82
+
83
+ end
84
+ end