vmc-tsuru 0.1.alpha

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,525 @@
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 'run'
270
+ usage('vmc run <appname> <command>')
271
+ set_cmd(:apps, :run, 2)
272
+ #
273
+ # when 'files'
274
+ # usage('vmc files <appname> [path] [--instance N] [--all] [--prefix]')
275
+ # if @args.size == 1
276
+ # set_cmd(:apps, :files, 1)
277
+ # else
278
+ # set_cmd(:apps, :files, 2)
279
+ # end
280
+ #
281
+ # when 'logs'
282
+ # usage('vmc logs <appname> [--instance N] [--all] [--prefix]')
283
+ # set_cmd(:apps, :logs, 1)
284
+ #
285
+ # when 'instances', 'scale'
286
+ # if @args.size > 1
287
+ # usage('vmc instances <appname> <num|delta>')
288
+ # set_cmd(:apps, :instances, 2)
289
+ # else
290
+ # usage('vmc instances <appname>')
291
+ # set_cmd(:apps, :instances, @args.size == 1 ? 1 : 0)
292
+ # end
293
+ #
294
+ # when 'crashes'
295
+ # usage('vmc crashes <appname>')
296
+ # set_cmd(:apps, :crashes, 1)
297
+ #
298
+ # when 'crashlogs'
299
+ # usage('vmc crashlogs <appname>')
300
+ # set_cmd(:apps, :crashlogs, 1)
301
+ #
302
+ when 'create'
303
+ usage('vmc create [appname] [--path PATH]')
304
+ #usage('vmc push [appname] [--path PATH] [--url URL] [--instances N] [--mem] [--runtime RUNTIME] [--no-start]')
305
+ if @args.size == 1
306
+ set_cmd(:apps, :push, 1)
307
+ else
308
+ set_cmd(:apps, :push, 0)
309
+ end
310
+
311
+ when 'push'
312
+ usage('vmc push <appname> [--path PATH]')
313
+ set_cmd(:apps, :update, @args.size == 1 ? 1 : 0)
314
+
315
+ when 'services'
316
+ usage('vmc services')
317
+ set_cmd(:services, :services)
318
+ #
319
+ # when 'env'
320
+ # usage('vmc env <appname>')
321
+ # set_cmd(:apps, :environment, 1)
322
+ #
323
+ # when 'env-add'
324
+ # usage('vmc env-add <appname> <variable[=]value>')
325
+ # if @args.size == 2
326
+ # set_cmd(:apps, :environment_add, 2)
327
+ # elsif @args.size == 3
328
+ # set_cmd(:apps, :environment_add, 3)
329
+ # end
330
+ #
331
+ # when 'env-del'
332
+ # usage('vmc env-del <appname> <variable>')
333
+ # set_cmd(:apps, :environment_del, 2)
334
+ #
335
+ when 'create-service', 'create_service'
336
+ usage('vmc create-service [service] [servicename] [appname] [--name servicename] [--bind appname]')
337
+ set_cmd(:services, :create_service) if @args.size == 0
338
+ set_cmd(:services, :create_service, 1) if @args.size == 1
339
+ set_cmd(:services, :create_service, 2) if @args.size == 2
340
+ set_cmd(:services, :create_service, 3) if @args.size == 3
341
+
342
+ when 'delete-service', 'delete_service'
343
+ usage('vmc delete-service <service>')
344
+ if @args.size == 1
345
+ set_cmd(:services, :delete_service, 1)
346
+ else
347
+ set_cmd(:services, :delete_service)
348
+ end
349
+
350
+ when 'bind-service', 'bind_service'
351
+ usage('vmc bind-service <servicename> <appname>')
352
+ set_cmd(:services, :bind_service, 2)
353
+
354
+ when 'unbind-service', 'unbind_service'
355
+ usage('vmc unbind-service <servicename> <appname>')
356
+ set_cmd(:services, :unbind_service, 2)
357
+
358
+ # when 'clone-services'
359
+ # usage('vmc clone-services <src-app> <dest-app>')
360
+ # set_cmd(:services, :clone_services, 2)
361
+ #
362
+ # when 'aliases'
363
+ # usage('vmc aliases')
364
+ # set_cmd(:misc, :aliases)
365
+ #
366
+ # when 'alias'
367
+ # usage('vmc alias <alias[=]command>')
368
+ # if @args.size == 1
369
+ # set_cmd(:misc, :alias, 1)
370
+ # elsif @args.size == 2
371
+ # set_cmd(:misc, :alias, 2)
372
+ # end
373
+ #
374
+ # when 'unalias'
375
+ # usage('vmc unalias <alias>')
376
+ # set_cmd(:misc, :unalias, 1)
377
+ #
378
+ # when 'tunnel'
379
+ # usage('vmc tunnel [servicename] [clientcmd] [--port port]')
380
+ # set_cmd(:services, :tunnel, 0) if @args.size == 0
381
+ # set_cmd(:services, :tunnel, 1) if @args.size == 1
382
+ # set_cmd(:services, :tunnel, 2) if @args.size == 2
383
+ #
384
+ # when 'rails-console'
385
+ # usage('vmc rails-console <appname>')
386
+ # set_cmd(:apps, :console, 1)
387
+ #
388
+ when 'help'
389
+ display_help if @args.size == 0
390
+ @help_only = true
391
+ parse_command!
392
+
393
+ when 'usage'
394
+ display basic_usage
395
+ exit(true)
396
+
397
+ when 'options'
398
+ # Simulate --options
399
+ @args = @args.unshift('--options')
400
+ parse_options!
401
+ #
402
+ # when 'manifest'
403
+ # usage('vmc manifest')
404
+ # set_cmd(:manifest, :edit)
405
+ #
406
+ # when 'extend-manifest'
407
+ # usage('vmc extend-manifest')
408
+ # set_cmd(:manifest, :extend, 1)
409
+ #
410
+ else
411
+ if verb
412
+ display "vmc: Unknown command [#{verb}]"
413
+ display basic_usage
414
+ exit(false)
415
+ end
416
+ end
417
+ end
418
+
419
+ def process_aliases!
420
+ aliases = VMC::Cli::Config.aliases
421
+ aliases.each_pair do |k,v|
422
+ if @args[0] == k
423
+ display "[#{@args[0]} aliased to #{aliases.invert[key]}]" if @options[:verbose]
424
+ @args[0] = v
425
+ break;
426
+ end
427
+ end
428
+ end
429
+
430
+ def usage(msg = nil)
431
+ @usage = msg if msg
432
+ @usage
433
+ end
434
+
435
+ def usage_error(msg = nil)
436
+ @usage_error = msg if msg
437
+ @usage_error
438
+ end
439
+
440
+ def run
441
+
442
+ trap('TERM') { print "\nTerminated\n"; exit(false)}
443
+
444
+ parse_options!
445
+
446
+ @options[:colorize] = false unless STDOUT.tty?
447
+
448
+ VMC::Cli::Config.colorize = @options.delete(:colorize)
449
+ VMC::Cli::Config.nozip = @options.delete(:nozip)
450
+ VMC::Cli::Config.trace = @options.delete(:trace)
451
+ VMC::Cli::Config.output ||= STDOUT unless @options[:quiet]
452
+
453
+ process_aliases!
454
+ parse_command!
455
+
456
+ if @namespace && @action
457
+ cmd = VMC::Cli::Command.const_get(@namespace.to_s.capitalize)
458
+ cmd.new(@options).send(@action, *@args.collect(&:dup))
459
+ elsif @help_only || @usage
460
+ display_usage
461
+ else
462
+ display basic_usage
463
+ exit(false)
464
+ end
465
+
466
+ rescue OptionParser::InvalidOption => e
467
+ puts(e.message.red)
468
+ puts("\n")
469
+ puts(basic_usage)
470
+ @exit_status = false
471
+ rescue OptionParser::AmbiguousOption => e
472
+ puts(e.message.red)
473
+ puts("\n")
474
+ puts(basic_usage)
475
+ @exit_status = false
476
+ rescue VMC::Client::AuthError => e
477
+ if VMC::Cli::Config.auth_token.nil?
478
+ puts "Login Required".red
479
+ else
480
+ puts "Not Authorized".red
481
+ end
482
+ @exit_status = false
483
+ rescue VMC::Client::TargetError, VMC::Client::NotFound, VMC::Client::BadTarget => e
484
+ puts e.message.red
485
+ puts e.backtrace
486
+ @exit_status = false
487
+ rescue VMC::Client::HTTPException => e
488
+ puts e.message.red
489
+ @exit_status = false
490
+ rescue VMC::Cli::GracefulExit => e
491
+ # Redirected commands end up generating this exception (kind of goto)
492
+ rescue VMC::Cli::CliExit => e
493
+ puts e.message.red
494
+ @exit_status = false
495
+ rescue VMC::Cli::CliError => e
496
+ say("Error #{e.error_code}: #{e.message}".red)
497
+ @exit_status = false
498
+ rescue SystemExit => e
499
+ @exit_status = e.success?
500
+ rescue SyntaxError => e
501
+ puts e.message.red
502
+ puts e.backtrace
503
+ @exit_status = false
504
+ rescue Interrupt => e
505
+ say("\nInterrupted".red)
506
+ @exit_status = false
507
+ rescue Exception => e
508
+ puts e.message.red
509
+ puts e.backtrace
510
+ @exit_status = false
511
+ ensure
512
+ say("\n")
513
+ @exit_status == true if @exit_status.nil?
514
+ if @options[:verbose]
515
+ if @exit_status
516
+ puts "[#{@namespace}:#{@action}] SUCCEEDED".green
517
+ else
518
+ puts "[#{@namespace}:#{@action}] FAILED".red
519
+ end
520
+ say("\n")
521
+ end
522
+ exit(@exit_status)
523
+ end
524
+
525
+ 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