jdc 0.1.1

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,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