jdc 0.1.1

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