paasio 0.3.16.beta.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/cli/runner.rb ADDED
@@ -0,0 +1,535 @@
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
+ opts.on('--remote REMOTE') { |r| @options[:remote] = r }
90
+ opts.on('--scm-type TYPE') { |t| @options[:scm_type] = t if t == 'git' || t == 'hg' }
91
+
92
+ # deprecated
93
+ opts.on('--exec EXEC') { |exec| @options[:exec] = exec }
94
+ opts.on('--noframework') { @options[:noframework] = true }
95
+ opts.on('--canary') { @options[:canary] = true }
96
+
97
+ # Proxying for another user, requires admin privileges
98
+ opts.on('-u PROXY') { |proxy| @options[:proxy] = proxy }
99
+
100
+ opts.on_tail('--options') { puts "#{opts}\n"; exit }
101
+ end
102
+ instances_delta_arg = check_instances_delta!
103
+ @args = opts_parser.parse!(@args)
104
+ @args.concat instances_delta_arg
105
+ convert_options!
106
+ self
107
+ end
108
+
109
+ def check_instances_delta!
110
+ return unless @args
111
+ instance_args = @args.select { |arg| /^[-]\d+$/ =~ arg } || []
112
+ @args.delete_if { |arg| instance_args.include? arg}
113
+ instance_args
114
+ end
115
+
116
+ def display_help
117
+ puts command_usage
118
+ exit
119
+ end
120
+
121
+ def convert_options!
122
+ # make sure certain options are valid and in correct form.
123
+ @options[:instances] = Integer(@options[:instances]) if @options[:instances]
124
+ end
125
+
126
+ def set_cmd(namespace, action, args_range=0)
127
+ return if @help_only
128
+ unless args_range == "*" || args_range.is_a?(Range)
129
+ args_range = (args_range.to_i..args_range.to_i)
130
+ end
131
+
132
+ if args_range == "*" || args_range.include?(@args.size)
133
+ @namespace = namespace
134
+ @action = action
135
+ else
136
+ @exit_status = false
137
+ if @args.size > args_range.last
138
+ usage_error("Too many arguments for [#{action}]: %s" % [ @args[args_range.last..-1].map{|a| "'#{a}'"}.join(', ') ])
139
+ else
140
+ usage_error("Not enough arguments for [#{action}]")
141
+ end
142
+ end
143
+ end
144
+
145
+ def parse_command!
146
+ # just return if already set, happends with -v, -h
147
+ return if @namespace && @action
148
+
149
+ verb = @args.shift
150
+ case verb
151
+
152
+ when 'version'
153
+ usage('paasio version')
154
+ set_cmd(:misc, :version)
155
+
156
+ when 'target'
157
+ usage('paasio target [url] [--url]')
158
+ if @args.size == 1
159
+ set_cmd(:misc, :set_target, 1)
160
+ else
161
+ set_cmd(:misc, :target)
162
+ end
163
+
164
+ when 'targets'
165
+ usage('paasio targets')
166
+ set_cmd(:misc, :targets)
167
+
168
+ when 'tokens'
169
+ usage('paasio tokens')
170
+ set_cmd(:misc, :tokens)
171
+
172
+ when 'info'
173
+ usage('paasio info')
174
+ set_cmd(:misc, :info)
175
+
176
+ when 'runtimes'
177
+ usage('paasio runtimes')
178
+ set_cmd(:misc, :runtimes)
179
+
180
+ when 'frameworks'
181
+ usage('paasio frameworks')
182
+ set_cmd(:misc, :frameworks)
183
+
184
+ when 'user'
185
+ usage('paasio user')
186
+ set_cmd(:user, :info)
187
+
188
+ when 'login'
189
+ usage('paasio login [email] [--email EMAIL] [--passwd PASS]')
190
+ if @args.size == 1
191
+ set_cmd(:user, :login, 1)
192
+ else
193
+ set_cmd(:user, :login)
194
+ end
195
+
196
+ when 'logout'
197
+ usage('paasio logout')
198
+ set_cmd(:user, :logout)
199
+
200
+ when 'passwd'
201
+ usage('paasio passwd')
202
+ if @args.size == 1
203
+ set_cmd(:user, :change_password, 1)
204
+ else
205
+ set_cmd(:user, :change_password)
206
+ end
207
+
208
+ when 'add-user', 'add_user', 'create_user', 'create-user', 'register'
209
+ usage('paasio add-user [user] [--email EMAIL] [--passwd PASS]')
210
+ if @args.size == 1
211
+ set_cmd(:admin, :add_user, 1)
212
+ else
213
+ set_cmd(:admin, :add_user)
214
+ end
215
+
216
+ when 'delete-user', 'delete_user', 'unregister'
217
+ usage('paasio delete-user <user>')
218
+ set_cmd(:admin, :delete_user, 1)
219
+
220
+ when 'users'
221
+ usage('paasio users')
222
+ set_cmd(:admin, :users)
223
+
224
+ when 'apps'
225
+ usage('paasio apps')
226
+ set_cmd(:apps, :apps)
227
+
228
+ when 'list'
229
+ usage('paasio list')
230
+ set_cmd(:apps, :list)
231
+
232
+ when 'start'
233
+ usage('paasio start <appname>')
234
+ set_cmd(:apps, :start, @args.size == 1 ? 1 : 0)
235
+
236
+ when 'stop'
237
+ usage('paasio stop <appname>')
238
+ set_cmd(:apps, :stop, @args.size == 1 ? 1 : 0)
239
+
240
+ when 'restart'
241
+ usage('paasio restart <appname>')
242
+ set_cmd(:apps, :restart, @args.size == 1 ? 1 : 0)
243
+
244
+ when 'rename'
245
+ usage('paasio rename <appname> <newname>')
246
+ set_cmd(:apps, :rename, 2)
247
+
248
+ when 'mem'
249
+ usage('paasio mem <appname> [memsize]')
250
+ if @args.size == 2
251
+ set_cmd(:apps, :mem, 2)
252
+ else
253
+ set_cmd(:apps, :mem, 1)
254
+ end
255
+
256
+ when 'stats'
257
+ usage('paasio stats <appname>')
258
+ set_cmd(:apps, :stats, @args.size == 1 ? 1 : 0)
259
+
260
+ when 'map'
261
+ usage('paasio map <appname> <url>')
262
+ set_cmd(:apps, :map, 2)
263
+
264
+ when 'unmap'
265
+ usage('paasio unmap <appname> <url>')
266
+ set_cmd(:apps, :unmap, 2)
267
+
268
+ when 'delete'
269
+ usage('paasio delete <appname>')
270
+ if @options[:all] && @args.size == 0
271
+ set_cmd(:apps, :delete)
272
+ else
273
+ set_cmd(:apps, :delete, 1)
274
+ end
275
+
276
+ when 'files'
277
+ usage('paasio files <appname> [path] [--instance N] [--all] [--prefix]')
278
+ if @args.size == 1
279
+ set_cmd(:apps, :files, 1)
280
+ else
281
+ set_cmd(:apps, :files, 2)
282
+ end
283
+
284
+ when 'logs'
285
+ usage('paasio logs <appname> [--instance N] [--all] [--prefix]')
286
+ set_cmd(:apps, :logs, 1)
287
+
288
+ when 'instances', 'scale'
289
+ if @args.size > 1
290
+ usage('paasio instances <appname> <num|delta>')
291
+ set_cmd(:apps, :instances, 2)
292
+ else
293
+ usage('paasio instances <appname>')
294
+ set_cmd(:apps, :instances, @args.size == 1 ? 1 : 0)
295
+ end
296
+
297
+ when 'crashes'
298
+ usage('paasio crashes <appname>')
299
+ set_cmd(:apps, :crashes, 1)
300
+
301
+ when 'crashlogs'
302
+ usage('paasio crashlogs <appname>')
303
+ set_cmd(:apps, :crashlogs, 1)
304
+
305
+ when 'create'
306
+ usage('paasio create [appname] [--url URL] [--instances N] [--mem] [--runtime RUNTIME] [--scm-type git|hg] [--remote NAME]')
307
+ if @args.size == 1
308
+ set_cmd(:apps, :create, 1)
309
+ else
310
+ set_cmd(:apps, :create, 0)
311
+ end
312
+
313
+ when 'upload'
314
+ usage('paasio upload <appname> [--path PATH]')
315
+ set_cmd(:apps, :upload, @args.size == 1 ? 1 : 0)
316
+
317
+ when 'push'
318
+ usage('paasio push [appname] [--path PATH] [--url URL] [--instances N] [--mem] [--runtime RUNTIME] [--no-start]')
319
+ if @args.size == 1
320
+ set_cmd(:apps, :push, 1)
321
+ else
322
+ set_cmd(:apps, :push, 0)
323
+ end
324
+
325
+ when 'update'
326
+ usage('paasio update <appname> [--path PATH]')
327
+ set_cmd(:apps, :update, @args.size == 1 ? 1 : 0)
328
+
329
+ when 'services'
330
+ usage('paasio services')
331
+ set_cmd(:services, :services)
332
+
333
+ when 'env'
334
+ usage('paasio env <appname>')
335
+ set_cmd(:apps, :environment, 1)
336
+
337
+ when 'env-add'
338
+ usage('paasio env-add <appname> <variable[=]value>')
339
+ if @args.size == 2
340
+ set_cmd(:apps, :environment_add, 2)
341
+ elsif @args.size == 3
342
+ set_cmd(:apps, :environment_add, 3)
343
+ end
344
+
345
+ when 'env-del'
346
+ usage('paasio env-del <appname> <variable>')
347
+ set_cmd(:apps, :environment_del, 2)
348
+
349
+ when 'create-service', 'create_service'
350
+ usage('paasio create-service [service] [servicename] [appname] [--name servicename] [--bind appname]')
351
+ set_cmd(:services, :create_service) if @args.size == 0
352
+ set_cmd(:services, :create_service, 1) if @args.size == 1
353
+ set_cmd(:services, :create_service, 2) if @args.size == 2
354
+ set_cmd(:services, :create_service, 3) if @args.size == 3
355
+
356
+ when 'delete-service', 'delete_service'
357
+ usage('paasio delete-service <service>')
358
+ if @args.size == 1
359
+ set_cmd(:services, :delete_service, 1)
360
+ else
361
+ set_cmd(:services, :delete_service)
362
+ end
363
+
364
+ when 'bind-service', 'bind_service'
365
+ usage('paasio bind-service <servicename> <appname>')
366
+ set_cmd(:services, :bind_service, 2)
367
+
368
+ when 'unbind-service', 'unbind_service'
369
+ usage('paasio unbind-service <servicename> <appname>')
370
+ set_cmd(:services, :unbind_service, 2)
371
+
372
+ when 'clone-services'
373
+ usage('paasio clone-services <src-app> <dest-app>')
374
+ set_cmd(:services, :clone_services, 2)
375
+
376
+ when 'aliases'
377
+ usage('paasio aliases')
378
+ set_cmd(:misc, :aliases)
379
+
380
+ when 'alias'
381
+ usage('paasio alias <alias[=]command>')
382
+ if @args.size == 1
383
+ set_cmd(:misc, :alias, 1)
384
+ elsif @args.size == 2
385
+ set_cmd(:misc, :alias, 2)
386
+ end
387
+
388
+ when 'unalias'
389
+ usage('paasio unalias <alias>')
390
+ set_cmd(:misc, :unalias, 1)
391
+
392
+ when 'tunnel'
393
+ usage('paasio tunnel [servicename] [clientcmd] [--port port]')
394
+ set_cmd(:services, :tunnel, 0) if @args.size == 0
395
+ set_cmd(:services, :tunnel, 1) if @args.size == 1
396
+ set_cmd(:services, :tunnel, 2) if @args.size == 2
397
+
398
+ when 'help'
399
+ display_help if @args.size == 0
400
+ @help_only = true
401
+ parse_command!
402
+
403
+ when 'usage'
404
+ display basic_usage
405
+ exit(true)
406
+
407
+ when 'options'
408
+ # Simulate --options
409
+ @args = @args.unshift('--options')
410
+ parse_options!
411
+
412
+ when 'manifest'
413
+ usage('paasio manifest')
414
+ set_cmd(:manifest, :edit)
415
+
416
+ when 'extend-manifest'
417
+ usage('paasio extend-manifest')
418
+ set_cmd(:manifest, :extend, 1)
419
+
420
+ else
421
+ if verb
422
+ display "paasio: Unknown command [#{verb}]"
423
+ display basic_usage
424
+ exit(false)
425
+ end
426
+ end
427
+ end
428
+
429
+ def process_aliases!
430
+ return if @args.empty?
431
+ aliases = VMC::Cli::Config.aliases
432
+ aliases.each_pair do |k,v|
433
+ if @args[0] == k
434
+ display "[#{@args[0]} aliased to #{aliases.invert[key]}]" if @options[:verbose]
435
+ @args[0] = v
436
+ break;
437
+ end
438
+ end
439
+ end
440
+
441
+ def usage(msg = nil)
442
+ @usage = msg if msg
443
+ @usage
444
+ end
445
+
446
+ def usage_error(msg = nil)
447
+ @usage_error = msg if msg
448
+ @usage_error
449
+ end
450
+
451
+ def run
452
+
453
+ trap('TERM') { print "\nTerminated\n"; exit(false)}
454
+
455
+ parse_options!
456
+
457
+ @options[:colorize] = false unless STDOUT.tty?
458
+
459
+ VMC::Cli::Config.colorize = @options.delete(:colorize)
460
+ VMC::Cli::Config.nozip = @options.delete(:nozip)
461
+ VMC::Cli::Config.trace = @options.delete(:trace)
462
+ VMC::Cli::Config.output ||= STDOUT unless @options[:quiet]
463
+
464
+ process_aliases!
465
+ parse_command!
466
+
467
+ if @namespace && @action
468
+ cmd = VMC::Cli::Command.const_get(@namespace.to_s.capitalize)
469
+ cmd.new(@options).send(@action, *@args.collect(&:dup))
470
+ elsif @help_only || @usage
471
+ display_usage
472
+ else
473
+ display basic_usage
474
+ exit(false)
475
+ end
476
+
477
+ rescue OptionParser::InvalidOption => e
478
+ puts(e.message.red)
479
+ puts("\n")
480
+ puts(basic_usage)
481
+ @exit_status = false
482
+ rescue OptionParser::AmbiguousOption => e
483
+ puts(e.message.red)
484
+ puts("\n")
485
+ puts(basic_usage)
486
+ @exit_status = false
487
+ rescue VMC::Client::AuthError => e
488
+ if VMC::Cli::Config.auth_token.nil?
489
+ puts "Login Required".red
490
+ else
491
+ puts "Not Authorized".red
492
+ end
493
+ @exit_status = false
494
+ rescue VMC::Client::TargetError, VMC::Client::NotFound, VMC::Client::BadTarget => e
495
+ puts e.message.red
496
+ @exit_status = false
497
+ rescue VMC::Client::HTTPException => e
498
+ puts e.message.red
499
+ @exit_status = false
500
+ rescue VMC::Cli::GracefulExit => e
501
+ # Redirected commands end up generating this exception (kind of goto)
502
+ rescue VMC::Cli::CliExit => e
503
+ puts e.message.red
504
+ @exit_status = false
505
+ rescue VMC::Cli::CliError => e
506
+ say("Error #{e.error_code}: #{e.message}".red)
507
+ @exit_status = false
508
+ rescue SystemExit => e
509
+ @exit_status = e.success?
510
+ rescue SyntaxError => e
511
+ puts e.message.red
512
+ puts e.backtrace
513
+ @exit_status = false
514
+ rescue Interrupt => e
515
+ say("\nInterrupted".red)
516
+ @exit_status = false
517
+ rescue Exception => e
518
+ puts e.message.red
519
+ puts e.backtrace
520
+ @exit_status = false
521
+ ensure
522
+ say("\n")
523
+ @exit_status == true if @exit_status.nil?
524
+ if @options[:verbose]
525
+ if @exit_status
526
+ puts "[#{@namespace}:#{@action}] SUCCEEDED".green
527
+ else
528
+ puts "[#{@namespace}:#{@action}] FAILED".red
529
+ end
530
+ say("\n")
531
+ end
532
+ exit(@exit_status)
533
+ end
534
+
535
+ 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