zillabyte-cli 0.0.24 → 0.1.0
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.
- checksums.yaml +6 -14
- data/lib/#zillabyte-cli.rb# +5 -0
- data/lib/zillabyte/api/apps.rb +16 -132
- data/lib/zillabyte/api/components.rb +115 -0
- data/lib/zillabyte/api/flows.rb +121 -0
- data/lib/zillabyte/api/keys.rb +70 -0
- data/lib/zillabyte/api.rb +15 -2
- data/lib/zillabyte/auth.rb +43 -16
- data/lib/zillabyte/cli/#logs.rb# +12 -0
- data/lib/zillabyte/cli/#repl.rb# +43 -0
- data/lib/zillabyte/cli/apps.rb +52 -893
- data/lib/zillabyte/cli/auth.rb +3 -8
- data/lib/zillabyte/cli/base.rb +28 -7
- data/lib/zillabyte/cli/components.rb +245 -0
- data/lib/zillabyte/cli/flows.rb +549 -0
- data/lib/zillabyte/cli/git.rb +38 -0
- data/lib/zillabyte/cli/help.rb +11 -4
- data/lib/zillabyte/cli/keys.rb +177 -0
- data/lib/zillabyte/cli/query.rb +0 -1
- data/lib/zillabyte/cli/relations.rb +2 -1
- data/lib/zillabyte/cli/templates/{js → apps/js}/simple_function.js +0 -0
- data/lib/zillabyte/cli/templates/{js → apps/js}/zillabyte.conf.yaml +0 -0
- data/lib/zillabyte/cli/templates/apps/python/app.py +17 -0
- data/lib/zillabyte/cli/templates/{python → apps/python}/requirements.txt +0 -0
- data/lib/zillabyte/cli/templates/{python → apps/python}/zillabyte.conf.yaml +1 -1
- data/lib/zillabyte/cli/templates/{ruby → apps/ruby}/Gemfile +0 -0
- data/lib/zillabyte/cli/templates/{ruby → apps/ruby}/app.rb +1 -1
- data/lib/zillabyte/cli/templates/{ruby → apps/ruby}/zillabyte.conf.yaml +0 -0
- data/lib/zillabyte/cli/templates/python/{simple_function.py → #simple_function.py#} +3 -6
- data/lib/zillabyte/common/session.rb +3 -1
- data/lib/zillabyte/helpers.rb +64 -1
- data/lib/zillabyte/runner/app_runner.rb +226 -0
- data/lib/zillabyte/runner/component_operation.rb +529 -0
- data/lib/zillabyte/runner/component_runner.rb +244 -0
- data/lib/zillabyte/runner/multilang_operation.rb +1133 -0
- data/lib/zillabyte/runner/operation.rb +11 -0
- data/lib/zillabyte/runner.rb +6 -0
- data/lib/zillabyte-cli/version.rb +1 -1
- data/zillabyte-cli.gemspec +1 -0
- metadata +83 -52
data/lib/zillabyte/cli/apps.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require "zillabyte/cli/
|
1
|
+
require "zillabyte/cli/flows"
|
2
2
|
require "zillabyte/cli/config"
|
3
|
+
require "zillabyte/runner/app_runner"
|
3
4
|
require "zillabyte/common"
|
4
5
|
require "pty"
|
5
6
|
require 'indentation'
|
@@ -11,7 +12,7 @@ require 'net/http'
|
|
11
12
|
|
12
13
|
# manage custom apps
|
13
14
|
#
|
14
|
-
class Zillabyte::Command::Apps < Zillabyte::Command::
|
15
|
+
class Zillabyte::Command::Apps < Zillabyte::Command::Flows
|
15
16
|
|
16
17
|
MAX_POLL_SECONDS = 60 * 15
|
17
18
|
POLL_SLEEP = 0.5
|
@@ -69,7 +70,7 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
69
70
|
res = api.request(
|
70
71
|
:expects => 200,
|
71
72
|
:method => :get,
|
72
|
-
:path => "/
|
73
|
+
:path => "/apps/#{app_id}/details",
|
73
74
|
:body => options.to_json
|
74
75
|
)
|
75
76
|
res.body
|
@@ -193,7 +194,7 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
193
194
|
def push
|
194
195
|
|
195
196
|
since = Time.now.utc.to_s
|
196
|
-
dir = options[:directory]
|
197
|
+
dir = options[:directory]
|
197
198
|
if dir.nil?
|
198
199
|
dir = Dir.pwd
|
199
200
|
else
|
@@ -236,44 +237,8 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
236
237
|
# $ zillabyte apps:pull .
|
237
238
|
#
|
238
239
|
def pull
|
239
|
-
|
240
|
-
app_id = options[:id] || shift_argument
|
241
|
-
|
242
|
-
if !(app_id =~ /^\d*$/)
|
243
|
-
options[:is_name] = true
|
244
|
-
end
|
245
|
-
|
246
|
-
dir = options[:directory] || shift_argument
|
247
|
-
error("no directory given", type) if dir.nil?
|
248
|
-
dir = File.expand_path(dir)
|
249
|
-
|
250
|
-
type = options[:output_type]
|
251
|
-
|
252
|
-
error("no id given", type) if app_id.nil?
|
253
|
-
|
254
|
-
# Create if not exists..
|
255
|
-
if File.exists?(dir)
|
256
|
-
if Dir.entries(dir).size != 2 and options[:force].nil?
|
257
|
-
error("target directory not empty. use --force to override", type)
|
258
|
-
end
|
259
|
-
else
|
260
|
-
FileUtils.mkdir_p(dir)
|
261
|
-
end
|
262
|
-
|
263
|
-
res = api.apps.pull_to_directory app_id, dir, session, options
|
264
|
-
|
265
|
-
if res['error']
|
266
|
-
error("error: #{res['error_message']}", type)
|
267
|
-
else
|
268
|
-
if type == "json"
|
269
|
-
display "{}"
|
270
|
-
else
|
271
|
-
display "app ##{res['id']} pulled to #{dir}"
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
240
|
+
super
|
275
241
|
end
|
276
|
-
alias_command "pull", "apps:pull"
|
277
242
|
|
278
243
|
|
279
244
|
# apps:delete ID
|
@@ -284,43 +249,7 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
284
249
|
# --output_type OUTPUT_TYPE # specify an output type i.e. json
|
285
250
|
#
|
286
251
|
def delete
|
287
|
-
|
288
|
-
|
289
|
-
if app_id.nil?
|
290
|
-
app_id = read_name_from_conf(options)
|
291
|
-
options[:is_name] = true
|
292
|
-
elsif !(app_id =~ /^\d*$/)
|
293
|
-
options[:is_name] = true
|
294
|
-
end
|
295
|
-
forced = options[:force]
|
296
|
-
type = options[:output_type]
|
297
|
-
|
298
|
-
if not forced
|
299
|
-
|
300
|
-
if !type.nil?
|
301
|
-
error("specify -f, --force to confirm deletion", type)
|
302
|
-
end
|
303
|
-
|
304
|
-
while true
|
305
|
-
|
306
|
-
display "This operation cannot be undone. Are you sure you want to delete this app? (yes/no):", false
|
307
|
-
confirm = ask
|
308
|
-
break if confirm == "yes" || confirm == "no"
|
309
|
-
display "Please enter 'yes' to delete the app or 'no' to exit"
|
310
|
-
end
|
311
|
-
end
|
312
|
-
|
313
|
-
confirmed = forced || confirm == "yes"
|
314
|
-
|
315
|
-
if confirmed
|
316
|
-
response = api.apps.delete(app_id, options)
|
317
|
-
if type == "json"
|
318
|
-
display "{}"
|
319
|
-
else
|
320
|
-
display response["body"]
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
252
|
+
super
|
324
253
|
end
|
325
254
|
|
326
255
|
|
@@ -332,86 +261,60 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
332
261
|
# --output_type OUTPUT_TYPE # specify an output type i.e. json
|
333
262
|
#
|
334
263
|
def prep
|
335
|
-
|
336
|
-
type = options[:output_type]
|
337
|
-
dir = options[:directory] || shift_argument
|
338
|
-
if dir.nil?
|
339
|
-
dir = Dir.pwd
|
340
|
-
else
|
341
|
-
dir = File.expand_path(dir)
|
342
|
-
end
|
343
|
-
options[:directory] = dir
|
344
|
-
meta = Zillabyte::CLI::Config.get_config_info(dir)
|
345
|
-
|
346
|
-
if meta.nil?
|
347
|
-
error("The specified directory (#{dir}) does not appear to contain a valid Zillabyte configuration file.", type)
|
348
|
-
end
|
349
|
-
|
350
|
-
case meta["language"]
|
351
|
-
when "ruby"
|
352
|
-
|
353
|
-
# Execute in the bundler context
|
354
|
-
full_script = File.join(dir, meta["script"])
|
355
|
-
cmd = "cd \"#{meta['home_dir']}\"; unset BUNDLE_GEMFILE; unset RUBYOPT; bundle install"
|
356
|
-
exec(cmd)
|
357
|
-
|
358
|
-
when "python"
|
359
|
-
vDir = "#{meta['home_dir']}/vEnv"
|
360
|
-
lock_file = meta['home_dir']+"/zillabyte_thread_lock_file"
|
361
|
-
if File.exists?(lock_file)
|
362
|
-
sleep(1) while File.exists?(lock_file)
|
363
|
-
else
|
364
|
-
begin
|
365
|
-
cmd = "touch #{lock_file}; virtualenv --clear --system-site-packages #{vDir}; PYTHONPATH=~/zb1/multilang/python/Zillabyte #{vDir}/bin/pip install -r #{meta['home_dir']}/requirements.txt"
|
366
|
-
system cmd, :out => :out
|
367
|
-
ensure
|
368
|
-
File.delete(lock_file)
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
when "js"
|
373
|
-
end
|
374
|
-
|
264
|
+
super
|
375
265
|
end
|
376
|
-
alias_command "prep", "apps:prep"
|
377
|
-
|
378
|
-
|
379
266
|
|
380
267
|
|
381
|
-
# apps:init [
|
268
|
+
# apps:init [NAME]
|
382
269
|
#
|
383
|
-
# initializes a new
|
384
|
-
# [LANG] defaults to ruby, and [DIR] to the current directory
|
270
|
+
# initializes a new app
|
385
271
|
#
|
386
|
-
# --
|
387
|
-
# --
|
272
|
+
# --lang LANG # which language to use [ruby, python]. default 'ruby'.
|
273
|
+
# --dir DIR # target directory of the app.
|
388
274
|
#
|
389
275
|
#Examples:
|
390
276
|
#
|
391
|
-
# $ zillabyte apps:init python
|
277
|
+
# $ zillabyte apps:init contact_extractor --lang python
|
392
278
|
#
|
393
279
|
def init
|
394
280
|
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
else
|
400
|
-
dir = File.expand_path(dir)
|
401
|
-
end
|
281
|
+
name = options[:name] || shift_argument
|
282
|
+
lang = options[:lang] || options[:language] || "ruby"
|
283
|
+
dir = options[:dir] || options[:directory] || File.join(Dir.pwd, name)
|
284
|
+
dir = File.expand_path(dir) unless dir.nil?
|
402
285
|
type = options[:output_type]
|
403
286
|
|
404
287
|
languages = ["ruby","python", "js"]
|
405
288
|
|
406
|
-
error("Unsupported language #{lang}.
|
289
|
+
error("Unsupported language #{lang}. Zillabyte currently supports #{languages.join(', ')}.", type) if not languages.include? lang
|
407
290
|
|
408
291
|
display "initializing empty #{lang} app in #{dir}" if type.nil?
|
409
|
-
|
292
|
+
erb_binding = binding
|
293
|
+
FileUtils.mkdir_p dir
|
294
|
+
|
295
|
+
Dir[File.join(File.expand_path("../templates/apps/#{lang}", __FILE__), "*")].each do |source_file|
|
296
|
+
|
297
|
+
next if File.directory?(source_file)
|
298
|
+
erb = ERB.new(File.read(source_file))
|
299
|
+
erb.filename = source_file
|
300
|
+
|
301
|
+
dest_file = File.join(dir, File.basename(source_file).gsub(/\.erb$/, ""))
|
302
|
+
File.open(dest_file, 'w') {|file| file.write(erb.result(erb_binding))}
|
303
|
+
|
304
|
+
end
|
410
305
|
|
411
306
|
|
412
307
|
end
|
413
308
|
|
414
309
|
|
310
|
+
# apps:errors ID
|
311
|
+
#
|
312
|
+
# show recent errors generated by the app
|
313
|
+
# --output_type OUTPUT_TYPE # specify an output type i.e. json
|
314
|
+
#
|
315
|
+
def errors
|
316
|
+
super
|
317
|
+
end
|
415
318
|
|
416
319
|
# apps:logs ID [OPERATION_NAME]
|
417
320
|
#
|
@@ -422,101 +325,9 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
422
325
|
# -v, --verbose LEVEL # sets the verbosity (error, info, debug) (default: info)
|
423
326
|
#
|
424
327
|
def logs
|
425
|
-
|
426
|
-
app_id = options[:id] || shift_argument
|
427
|
-
if app_id.nil?
|
428
|
-
app_id = read_name_from_conf(options)
|
429
|
-
options[:is_name] = true
|
430
|
-
elsif !(app_id =~ /^\d*$/)
|
431
|
-
options[:is_name] = true
|
432
|
-
end
|
433
|
-
|
434
|
-
operation_id = options[:operation] || shift_argument || '_ALL_'
|
435
|
-
category = options[:verbose] || '_ALL_'
|
436
|
-
type = options[:output_type]
|
437
|
-
|
438
|
-
carry_settings = {
|
439
|
-
:category => category
|
440
|
-
}
|
441
|
-
|
442
|
-
display "Retrieving logs for app ##{app_id}...please wait..." if type.nil?
|
443
|
-
hash = self.api.logs.get(app_id, operation_id, options)
|
444
|
-
|
445
|
-
fetch_logs(hash, operation_id)
|
446
|
-
|
328
|
+
super
|
447
329
|
end
|
448
|
-
alias_command "logs", "apps:logs"
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
# apps:errors ID
|
453
|
-
#
|
454
|
-
# show recent errors generated by the app
|
455
|
-
# --output_type OUTPUT_TYPE # specify an output type i.e. json
|
456
|
-
#
|
457
|
-
def errors
|
458
|
-
|
459
|
-
# Init
|
460
|
-
app_id = options[:id] || shift_argument
|
461
|
-
|
462
|
-
# No name?
|
463
|
-
if app_id.nil?
|
464
|
-
app_id = read_name_from_conf(options)
|
465
|
-
options[:is_name] = true
|
466
|
-
elsif !(app_id =~ /^\d*$/)
|
467
|
-
options[:is_name] = true
|
468
|
-
end
|
469
|
-
|
470
|
-
type = options[:output_type]
|
471
|
-
|
472
|
-
# Make the request
|
473
|
-
res = api.request(
|
474
|
-
:expects => 200,
|
475
|
-
:method => :get,
|
476
|
-
:body => options.to_json,
|
477
|
-
:path => "/flows/#{CGI.escape(app_id)}/errors"
|
478
|
-
)
|
479
|
-
|
480
|
-
# Render
|
481
|
-
display "Recent errors:" if type.nil?
|
482
|
-
headings = ["operation", "date", "error"]
|
483
|
-
rows = (res.body["recent_errors"] || []).map do |row|
|
484
|
-
if row['date']
|
485
|
-
d = Time.at(row['date']/1000)
|
486
|
-
else
|
487
|
-
d = nil
|
488
|
-
end
|
489
|
-
[row['name'], d, row['message']]
|
490
|
-
end
|
491
|
-
rows.sort! do |a,b|
|
492
|
-
a[1] <=> b[1]
|
493
|
-
end
|
494
|
-
color_map = {}
|
495
|
-
colors = LogFormatter::COLORS.clone
|
496
|
-
rows.each do |row|
|
497
|
-
name = row[0]
|
498
|
-
time = row[1]
|
499
|
-
message = row[2].strip
|
500
|
-
color_map[name] ||= colors.shift
|
501
|
-
if time
|
502
|
-
display "#{"* #{name} - #{time_ago_in_words(time)} ago".colorize(color_map[name])}:" if type.nil?
|
503
|
-
else
|
504
|
-
display "#{"* #{name}".colorize(color_map[name])}:" if type.nil?
|
505
|
-
end
|
506
|
-
message.split('\n').each do |sub_line|
|
507
|
-
display " #{sub_line}" if type.nil?
|
508
|
-
end
|
509
|
-
end
|
510
|
-
|
511
|
-
end
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
330
|
|
517
|
-
|
518
|
-
|
519
|
-
|
520
331
|
|
521
332
|
# apps:cycles ID [OPTIONS]
|
522
333
|
#
|
@@ -565,11 +376,11 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
565
376
|
|
566
377
|
if response["job_id"]
|
567
378
|
options[:job_id] = response["job_id"]
|
568
|
-
app_id = response["
|
379
|
+
app_id = response["app_id"]
|
569
380
|
options.delete :is_name
|
570
381
|
|
571
382
|
start = Time.now.utc
|
572
|
-
display "Next cycle request sent. If your app was RETIRED this may take
|
383
|
+
display "Next cycle request sent. If your app was RETIRED this may take a few minutes. Check 'zillabyte logs' for progress." if type.nil?
|
573
384
|
|
574
385
|
while(Time.now.utc < start + MAX_POLL_SECONDS) do
|
575
386
|
|
@@ -605,475 +416,19 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
605
416
|
end
|
606
417
|
|
607
418
|
|
608
|
-
# apps:test
|
419
|
+
# apps:test
|
609
420
|
#
|
610
421
|
# tests a local app with sample data
|
611
422
|
#
|
612
|
-
# --config CONFIG_FILE
|
613
|
-
# --output OUTPUT_FILE
|
614
|
-
# --
|
615
|
-
# --
|
616
|
-
# --
|
423
|
+
# --config CONFIG_FILE # use the given config file
|
424
|
+
# --output OUTPUT_FILE # writes sink output to a file
|
425
|
+
# --cycles CYCLES # number of cycles to emit (default 1)
|
426
|
+
# --directory DIR # app directory
|
427
|
+
# -i, --interactive # allow user to control input and read output
|
617
428
|
#
|
618
429
|
def test
|
619
|
-
|
620
|
-
output = options[:output]
|
621
|
-
otype = options[:output_type] #type is used below for something else
|
622
|
-
|
623
|
-
max_seconds = (options[:wait] || "30").to_i
|
624
|
-
batches = (options[:batches] || "1").to_i
|
625
|
-
|
626
|
-
def read_message(read_stream, color)
|
627
|
-
msg = nil
|
628
|
-
read_stream.each do |line|
|
629
|
-
line.strip!
|
630
|
-
if(line == "end")
|
631
|
-
return msg
|
632
|
-
end
|
633
|
-
begin
|
634
|
-
hash = JSON.parse(line)
|
635
|
-
if(hash["command"] == "done")
|
636
|
-
msg = "done"
|
637
|
-
else
|
638
|
-
msg = hash.to_json
|
639
|
-
end
|
640
|
-
rescue
|
641
|
-
next
|
642
|
-
end
|
643
|
-
end
|
644
|
-
msg
|
645
|
-
end
|
646
|
-
|
647
|
-
def write_message(write_stream, msg)
|
648
|
-
write_stream.write msg.strip + "\n"
|
649
|
-
write_stream.write "end\n"
|
650
|
-
write_stream.flush
|
651
|
-
end
|
652
|
-
|
653
|
-
def truncate_message(msg)
|
654
|
-
return msg if(!msg.instance_of?(String))
|
655
|
-
t_length = 50 # truncates entries to this length
|
656
|
-
m_length = msg.length
|
657
|
-
msg_out = m_length > t_length ? msg[0..t_length-3]+"..." : msg
|
658
|
-
msg_out
|
659
|
-
end
|
660
|
-
|
661
|
-
def handshake(write_stream, read_stream, node, color)
|
662
|
-
begin
|
663
|
-
write_message write_stream, "{\"pidDir\": \"/tmp\"}\n"
|
664
|
-
read_message read_stream, color # Read to "end\n"
|
665
|
-
rescue Exception => e
|
666
|
-
puts "Error handshaking node: #{node}"
|
667
|
-
raise e
|
668
|
-
end
|
669
|
-
end
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
# INIT
|
674
|
-
dir = options[:directory] || shift_argument
|
675
|
-
if dir.nil?
|
676
|
-
dir = Dir.pwd
|
677
|
-
else
|
678
|
-
dir = File.expand_path(dir)
|
679
|
-
end
|
680
|
-
options[:directory] = dir
|
681
|
-
|
682
|
-
meta = Zillabyte::API::Apps.get_rich_meta_info_from_script(dir, self, {:test => true})
|
683
|
-
if meta.nil?
|
684
|
-
error "this is not a valid zillabyte app directory"
|
685
|
-
exit
|
686
|
-
end
|
687
|
-
|
688
|
-
# Show the user what we know about their app...
|
689
|
-
display "inferring your app details..."
|
690
|
-
colors = {}
|
691
|
-
describe_app(meta, colors)
|
692
|
-
|
693
|
-
|
694
|
-
# Extract the app's information..
|
695
|
-
nodes = meta["nodes"]
|
696
|
-
write_to_next_each = []
|
697
|
-
write_queue = []
|
698
|
-
stream_messages = {}
|
699
|
-
default_stream = "_default"
|
700
|
-
|
701
|
-
# Iterate all nodes sequentially and invoke them in separate processes...
|
702
|
-
nodes.each do |node|
|
703
|
-
|
704
|
-
# Init
|
705
|
-
type = node["type"]
|
706
|
-
name = node["name"]
|
707
|
-
consumes = node["consumes"]
|
708
|
-
emits = node["emits"]
|
709
|
-
|
710
|
-
color = colors[name] || :default
|
711
|
-
|
712
|
-
op_display = lambda do |msg, override_color = nil|
|
713
|
-
display "#{name} - #{msg}".colorize(override_color || color)
|
714
|
-
end
|
715
|
-
|
716
|
-
# A Source?
|
717
|
-
if type == "source"
|
718
|
-
|
719
|
-
# A source from relation?
|
720
|
-
if node['matches'] or node["relation"]
|
721
|
-
matches = node['matches'] || (node["relation"]["query"])
|
722
|
-
emits = emits.first #For spouting from a relation, there should only be one emits
|
723
|
-
op_display.call "Grabbing remote data"
|
724
|
-
|
725
|
-
res = api.query.agnostic(matches)
|
726
|
-
rows = res["rows"]
|
727
|
-
column_aliases = res["column_aliases"]
|
728
|
-
|
729
|
-
|
730
|
-
if(rows.nil? or rows.length == 0)
|
731
|
-
raise NameError, "Could not find data that matches your 'matches' clause"
|
732
|
-
end
|
733
|
-
rows.each do |tuple|
|
734
|
-
values = {}
|
735
|
-
meta = {}
|
736
|
-
tuple.each do |k, v|
|
737
|
-
if(k == "id")
|
738
|
-
next
|
739
|
-
elsif(k == "confidence" or k == "since" or k == "source")
|
740
|
-
meta[k] = v
|
741
|
-
else
|
742
|
-
values[k] = v
|
743
|
-
end
|
744
|
-
end
|
745
|
-
read_msg = {"tuple" => values, "meta" => meta, "column_aliases" => column_aliases}.to_json
|
746
|
-
values = Hash[values.map{|k, v| [truncate_message(k), truncate_message(v)]}]
|
747
|
-
op_display.call "emitted: #{values} #{meta} to #{emits}"
|
748
|
-
stream_messages[emits] ||= []
|
749
|
-
stream_messages[emits] << read_msg
|
750
|
-
end
|
751
|
-
|
752
|
-
# Done processing...
|
753
|
-
next
|
754
|
-
|
755
|
-
else
|
756
|
-
|
757
|
-
# A regular source..
|
758
|
-
stream_messages[default_stream] ||= []
|
759
|
-
stream_messages[default_stream] << "{\"command\": \"prepare\"}\n"
|
760
|
-
stream_messages[default_stream] << "{\"command\": \"begin_cycle\"}\n"
|
761
|
-
emits.each {|ss| stream_messages[ss] = []} #initialize streams
|
762
|
-
stream_size_at_last_call_to_next_tuple = Hash[emits.map {|ss| [ss, 0]}] #initialize initial size of streams (all 0)
|
763
|
-
# the above initializations are used to deal with the case where end_cycle_policy == "null_emit"
|
764
|
-
n_batches_emitted = 1
|
765
|
-
end_cycle_received = false
|
766
|
-
last_call_next_tuple = false
|
767
|
-
|
768
|
-
end
|
769
|
-
|
770
|
-
# An Aggregate?
|
771
|
-
elsif type == "aggregate"
|
772
|
-
if node['consumes']
|
773
|
-
input_stream = node['consumes']
|
774
|
-
else
|
775
|
-
input_stream = stream_messages.keys.first
|
776
|
-
end
|
777
|
-
messages = stream_messages[input_stream] || []
|
778
|
-
stream_messages[input_stream] = []
|
779
|
-
|
780
|
-
group_by = node['group_by']
|
781
|
-
group_tuples = {}
|
782
|
-
messages.each do |msg|
|
783
|
-
msg = JSON.parse(msg)
|
784
|
-
tuple = msg["tuple"].to_json
|
785
|
-
meta = msg["meta"].to_json
|
786
|
-
column_aliases = msg["column_aliases"] || {}
|
787
|
-
aliases = Hash[column_aliases.map{|h| [h["alias"],h["concrete_name"]]}]
|
788
|
-
gt = {}
|
789
|
-
group_by.each do |field|
|
790
|
-
field_name = aliases[field] || field
|
791
|
-
gt[field] = msg["tuple"][field_name]
|
792
|
-
end
|
793
|
-
|
794
|
-
msg_no_brackets = "\"tuple\": #{tuple}, \"meta\": #{meta}, \"column_aliases\": #{column_aliases.to_json}"
|
795
|
-
if group_tuples[gt]
|
796
|
-
group_tuples[gt] << msg_no_brackets
|
797
|
-
else
|
798
|
-
group_tuples[gt] = [msg_no_brackets]
|
799
|
-
end
|
800
|
-
end
|
801
|
-
|
802
|
-
group_tuples.each do |group_tuple, tuples|
|
803
|
-
stream_messages[input_stream] << "{\"command\": \"begin_group\", \"tuple\": #{group_tuple.to_json}, \"meta\":{}}\n"
|
804
|
-
tuples.each do |t|
|
805
|
-
stream_messages[input_stream] << "{\"command\": \"aggregate\", #{t}}\n"
|
806
|
-
end
|
807
|
-
stream_messages[input_stream] << "{\"command\": \"end_group\"}\n"
|
808
|
-
end
|
809
|
-
|
810
|
-
# A Sink?
|
811
|
-
elsif type == "sink"
|
812
|
-
|
813
|
-
if consumes.nil?
|
814
|
-
error "The node #{name} must declare which stream it 'consumes'"
|
815
|
-
end
|
816
|
-
messages = stream_messages[consumes] || []
|
817
|
-
|
818
|
-
table = Terminal::Table.new :title => name
|
819
|
-
csv_str = CSV.generate do |csv|
|
820
|
-
header_written = false;
|
821
|
-
messages.each do |msg|
|
822
|
-
obj = JSON.parse(msg)
|
823
|
-
t = obj['tuple']
|
824
|
-
m = obj['meta'] || {}
|
825
|
-
if t
|
826
|
-
if header_written == false
|
827
|
-
keys = [t.keys, m.keys].flatten
|
828
|
-
csv << keys
|
829
|
-
table << keys
|
830
|
-
table << :separator
|
831
|
-
header_written = true
|
832
|
-
end
|
833
|
-
vals = [t.values, m.values].flatten
|
834
|
-
csv << vals
|
835
|
-
table << vals
|
836
|
-
end
|
837
|
-
end
|
838
|
-
end
|
839
|
-
|
840
|
-
display table.to_s.colorize(color)
|
841
|
-
|
842
|
-
if output
|
843
|
-
filename = "#{output}.csv"
|
844
|
-
f = File.open(filename, "w")
|
845
|
-
f.write(csv_str)
|
846
|
-
f.close()
|
847
|
-
op_display.call "output written to #{filename}"
|
848
|
-
end
|
849
|
-
|
850
|
-
next
|
851
|
-
end
|
852
|
-
|
853
|
-
|
854
|
-
cmd = command("--execute_live --name #{name}", otype, dir)
|
855
|
-
begin
|
856
|
-
|
857
|
-
# Start the operation...
|
858
|
-
op_display.call "beginning #{type} #{name}"
|
859
|
-
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thread|
|
860
|
-
begin
|
861
|
-
|
862
|
-
# Init
|
863
|
-
handshake stdin, stdout, node, color
|
864
|
-
write_queue = []
|
865
|
-
read_queue = []
|
866
|
-
mutex = Mutex.new
|
867
|
-
signal = ConditionVariable.new
|
868
|
-
|
869
|
-
if consumes.nil?
|
870
|
-
# Assume default stream (this should only happen for the source)
|
871
|
-
stream_name = default_stream
|
872
|
-
else
|
873
|
-
stream_name = consumes
|
874
|
-
end
|
875
|
-
write_queue = stream_messages[stream_name].clone
|
876
|
-
stream_messages.delete(stream_name)
|
877
|
-
|
878
|
-
# Start writing the messages...
|
879
|
-
writing_thread = Thread.start do
|
880
|
-
|
881
|
-
while(true)
|
882
|
-
|
883
|
-
case type
|
884
|
-
when 'source'
|
885
|
-
break if n_batches_emitted > batches
|
886
|
-
if write_queue.empty?
|
887
|
-
sleep 0.5
|
888
|
-
next
|
889
|
-
end
|
890
|
-
else
|
891
|
-
break if write_queue.empty?
|
892
|
-
end
|
893
|
-
|
894
|
-
# Make sure we're not reading anything...
|
895
|
-
while(write_queue.size == 0)
|
896
|
-
mutex.synchronize do
|
897
|
-
signal.wait(mutex)
|
898
|
-
end
|
899
|
-
end
|
900
|
-
|
901
|
-
# Get next mesage
|
902
|
-
write_msg = write_queue.shift
|
903
|
-
|
904
|
-
# Make it human-understable
|
905
|
-
write_json = JSON.parse(write_msg)
|
906
|
-
if write_json['tuple']
|
907
|
-
display_hash = Hash[write_json['tuple'].map{|k, v| [truncate_message(k), truncate_message(v)]}]
|
908
|
-
op_display.call "receiving: #{display_hash}"
|
909
|
-
elsif write_json['command'] == 'next'
|
910
|
-
last_call_next_tuple = true
|
911
|
-
op_display.call "asking for next tuple(s)"
|
912
|
-
else
|
913
|
-
# puts write_json
|
914
|
-
end
|
915
|
-
|
916
|
-
# Actually send it to the process
|
917
|
-
begin
|
918
|
-
write_message stdin, write_msg
|
919
|
-
sleep 0.1
|
920
|
-
rescue Exception => e
|
921
|
-
puts "Error running #{cmd}: #{e}"
|
922
|
-
raise e
|
923
|
-
end
|
924
|
-
end
|
925
|
-
end
|
926
|
-
|
927
|
-
# Start reading messages...
|
928
|
-
reading_thread = Thread.start do
|
929
|
-
while(true)
|
930
|
-
|
931
|
-
# If the end cycle command is received, we either trigger the next cycle if the number of emitted
|
932
|
-
# cycles is less than what the user requested, or we break
|
933
|
-
if type == "source" and end_cycle_received
|
934
|
-
|
935
|
-
mutex.synchronize do
|
936
|
-
write_queue << "{\"command\": \"begin_cycle\"}\n"
|
937
|
-
signal.signal()
|
938
|
-
end
|
939
|
-
|
940
|
-
n_batches_emitted += 1
|
941
|
-
end_cycle_received = false
|
942
|
-
last_call_next_tuple = false
|
943
|
-
break if n_batches_emitted > batches
|
944
|
-
sleep 0.5
|
945
|
-
next
|
946
|
-
end
|
947
|
-
|
948
|
-
# Get next message
|
949
|
-
read_msg = read_message(stdout, color);
|
950
|
-
|
951
|
-
if read_msg == "done" || read_msg.nil?
|
952
|
-
|
953
|
-
# For sources, if we receive a "done", check to see if any of the streams emitted by the source has
|
954
|
-
# increased in size since the last call to next_tuple. If so, the cycle isn't over, otherwise, the
|
955
|
-
# current call to next_tuple emitted nothing and if the end_cycle_policy is set to null_emit, this
|
956
|
-
# should end the current cycle.
|
957
|
-
|
958
|
-
if type == "source"
|
959
|
-
if last_call_next_tuple and node["end_cycle_policy"] == "null_emit"
|
960
|
-
end_cycle_received = true
|
961
|
-
emits.each do |ss|
|
962
|
-
end_cycle_received = false if stream_messages[ss].size > stream_size_at_last_call_to_next_tuple[ss]
|
963
|
-
break
|
964
|
-
end
|
965
|
-
next if end_cycle_received
|
966
|
-
end
|
967
|
-
|
968
|
-
# If the policy isn't "null_emit", then just request next_tuple again
|
969
|
-
mutex.synchronize do
|
970
|
-
write_queue << "{\"command\": \"next\"}\n"
|
971
|
-
signal.signal()
|
972
|
-
end
|
973
|
-
|
974
|
-
end
|
975
|
-
|
976
|
-
# For other operations, if the queue is empty then we're done
|
977
|
-
if write_queue.empty?
|
978
|
-
break # exit while loop
|
979
|
-
else
|
980
|
-
sleep 0.5 # spin wait
|
981
|
-
next
|
982
|
-
end
|
983
|
-
end
|
984
|
-
|
985
|
-
# Process message
|
986
|
-
obj = JSON.parse(read_msg)
|
987
|
-
|
988
|
-
# process the received tuple or other commands
|
989
|
-
if obj['tuple']
|
990
|
-
|
991
|
-
# if
|
992
|
-
tt = obj['tuple']
|
993
|
-
tt.each do |kk, vv|
|
994
|
-
if tt[kk].nil? and type == "source" and node["end_cycle_policy"] == "null_emit"
|
995
|
-
end_cycle_received = true
|
996
|
-
# read rest of stuff in stdout buffer until "done"
|
997
|
-
mm = nil
|
998
|
-
while(mm != "done")
|
999
|
-
mm = read_message(stdout, color)
|
1000
|
-
end
|
1001
|
-
break
|
1002
|
-
end
|
1003
|
-
end
|
1004
|
-
next if end_cycle_received
|
1005
|
-
|
1006
|
-
# Convert to a incoming tuple for the next operation
|
1007
|
-
next_msg = {
|
1008
|
-
:tuple => obj['tuple'],
|
1009
|
-
:meta => obj['meta']
|
1010
|
-
}
|
1011
|
-
emit_stream = obj['stream']
|
1012
|
-
stream_messages[emit_stream] ||= []
|
1013
|
-
stream_messages[emit_stream] << next_msg.to_json
|
1014
|
-
|
1015
|
-
display_hash = Hash[obj['tuple'].map{|k, v| [truncate_message(k), truncate_message(v)]}]
|
1016
|
-
op_display.call "emitted: #{display_hash} to #{emit_stream}"
|
1017
|
-
|
1018
|
-
# track stream message size to end cycles when necessary
|
1019
|
-
stream_size_at_last_call_to_next_tuple[emit_stream] = stream_messages[emit_stream].size if type == "source"
|
1020
|
-
elsif obj['command'] == 'end_cycle'
|
1021
|
-
end_cycle_received = true
|
1022
|
-
# command:end_cycle should always be followed by done, read it (below) so
|
1023
|
-
# that it doesn't interfere with next
|
1024
|
-
read_message(stdout, color)
|
1025
|
-
elsif obj['command'] == 'log'
|
1026
|
-
op_display.call "log: #{obj['msg']}"
|
1027
|
-
elsif obj['command'] == 'fail'
|
1028
|
-
op_display.call "error: #{obj['msg']}", :red
|
1029
|
-
exit(1)
|
1030
|
-
elsif obj['ping']
|
1031
|
-
|
1032
|
-
mutex.synchronize do
|
1033
|
-
write_queue << "{\"pong\": \"#{Time.now.utc.to_f}\"}\n"
|
1034
|
-
signal.signal()
|
1035
|
-
end
|
1036
|
-
|
1037
|
-
else
|
1038
|
-
error "unknown message: #{read_msg}"
|
1039
|
-
end
|
1040
|
-
|
1041
|
-
end
|
1042
|
-
end
|
1043
|
-
|
1044
|
-
# stderr thread
|
1045
|
-
stderr_thread = Thread.start do
|
1046
|
-
stderr.each do |line|
|
1047
|
-
op_display.call("stderr: #{line}", :red)
|
1048
|
-
end
|
1049
|
-
end
|
1050
|
-
|
1051
|
-
begin
|
1052
|
-
killed = Timeout.timeout(max_seconds) do
|
1053
|
-
reading_thread.join()
|
1054
|
-
writing_thread.join()
|
1055
|
-
stderr_thread.kill()
|
1056
|
-
op_display.call "completed #{type} #{name}"
|
1057
|
-
end
|
1058
|
-
rescue Timeout::Error
|
1059
|
-
op_display.call "max time reached. preempting #{type} #{name}. set --wait to increase", :red
|
1060
|
-
reading_thread.kill() if reading_thread.alive?
|
1061
|
-
writing_thread.kill() if writing_thread.alive?
|
1062
|
-
stderr_thread.kill() if stderr_thread.alive?
|
1063
|
-
end
|
1064
|
-
|
1065
|
-
rescue Errno::EIO
|
1066
|
-
puts "Errno:EIO error, but this probably just means " +
|
1067
|
-
"that the process has finished giving output"
|
1068
|
-
end
|
1069
|
-
end
|
1070
|
-
rescue PTY::ChildExited
|
1071
|
-
puts "The child process exited!"
|
1072
|
-
end
|
1073
|
-
end
|
1074
|
-
|
430
|
+
super
|
1075
431
|
end
|
1076
|
-
alias_command "test", "apps:test"
|
1077
432
|
|
1078
433
|
|
1079
434
|
# apps:kill ID
|
@@ -1084,7 +439,6 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
1084
439
|
# --output_type OUTPUT_TYPE # specify an output type i.e. json
|
1085
440
|
#
|
1086
441
|
def kill
|
1087
|
-
|
1088
442
|
id = options[:id] || shift_argument
|
1089
443
|
type = options[:output_type]
|
1090
444
|
|
@@ -1103,7 +457,6 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
1103
457
|
else
|
1104
458
|
display "App ##{id} killed"
|
1105
459
|
end
|
1106
|
-
|
1107
460
|
end
|
1108
461
|
|
1109
462
|
|
@@ -1117,32 +470,8 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
1117
470
|
#
|
1118
471
|
# HIDDEN:
|
1119
472
|
def live_run
|
1120
|
-
|
1121
|
-
name = options[:name] || shift_argument
|
1122
|
-
type = options[:output_type]
|
1123
|
-
|
1124
|
-
thread_id = options[:thread] || shift_argument || ""
|
1125
|
-
dir = options[:directory] || shift_argument
|
1126
|
-
if dir.nil?
|
1127
|
-
dir = Dir.pwd
|
1128
|
-
else
|
1129
|
-
dir = File.expand_path(dir)
|
1130
|
-
end
|
1131
|
-
options[:directory] = dir
|
1132
|
-
|
1133
|
-
meta = Zillabyte::CLI::Config.get_config_info(dir, options)
|
1134
|
-
|
1135
|
-
if meta.nil?
|
1136
|
-
error("could not find meta information for: #{dir}", type)
|
1137
|
-
end
|
1138
|
-
|
1139
|
-
if(thread_id == "")
|
1140
|
-
exec(command("--execute_live --name #{name.to_s}",type, dir))
|
1141
|
-
else
|
1142
|
-
exec(command("--execute_live --name #{name.to_s} --pipe #{dir}/#{thread_id}",type, dir))
|
1143
|
-
end
|
473
|
+
super
|
1144
474
|
end
|
1145
|
-
alias_command "live_run", "apps:live_run"
|
1146
475
|
|
1147
476
|
|
1148
477
|
|
@@ -1154,36 +483,11 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
1154
483
|
# --directory DIR # Directory of the app
|
1155
484
|
#
|
1156
485
|
def status
|
1157
|
-
|
1158
|
-
id = options[:id] || shift_argument
|
1159
|
-
type = options[:output_type]
|
1160
|
-
|
1161
|
-
if id.nil?
|
1162
|
-
id = read_name_from_conf(options)
|
1163
|
-
options[:is_name] = true
|
1164
|
-
elsif !(id =~ /^\d*$/)
|
1165
|
-
options[:is_name] = true
|
1166
|
-
end
|
1167
|
-
|
1168
|
-
res = api.request(
|
1169
|
-
:expects => 200,
|
1170
|
-
:method => :get,
|
1171
|
-
:path => "/flows/#{id}"
|
1172
|
-
)
|
1173
|
-
res.body
|
1174
|
-
|
1175
|
-
if type == "json"
|
1176
|
-
display res.body.to_json
|
1177
|
-
else
|
1178
|
-
# TODO
|
1179
|
-
display res.body.to_json
|
1180
|
-
end
|
1181
|
-
|
486
|
+
super
|
1182
487
|
end
|
1183
488
|
|
1184
489
|
|
1185
490
|
|
1186
|
-
|
1187
491
|
# apps:info [DIR]
|
1188
492
|
#
|
1189
493
|
# outputs the info for the app in the dir.
|
@@ -1193,116 +497,13 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
1193
497
|
# --directory DIR # Directory of the app
|
1194
498
|
#
|
1195
499
|
def info
|
1196
|
-
|
1197
|
-
dir = options[:directory] || shift_argument
|
1198
|
-
if dir.nil?
|
1199
|
-
dir = Dir.pwd
|
1200
|
-
else
|
1201
|
-
dir = File.expand_path(dir)
|
1202
|
-
end
|
1203
|
-
options[:directory] = dir
|
1204
|
-
|
1205
|
-
info_file = "#{dir}/#{SecureRandom.uuid}"
|
1206
|
-
type = options[:output_type]
|
1207
|
-
|
1208
|
-
cmd = command("--info --file #{info_file}", type, dir)
|
1209
|
-
app_info = Zillabyte::Command::Apps.get_info(cmd, info_file, dir, options)
|
1210
|
-
|
1211
|
-
if type == "json"
|
1212
|
-
puts app_info
|
1213
|
-
else
|
1214
|
-
if options[:pretty]
|
1215
|
-
puts JSON.pretty_generate(JSON.parse(app_info))
|
1216
|
-
else
|
1217
|
-
puts app_info
|
1218
|
-
end
|
1219
|
-
end
|
1220
|
-
|
1221
|
-
exit
|
500
|
+
super
|
1222
501
|
end
|
1223
|
-
alias_command "info", "apps:info"
|
1224
|
-
|
1225
|
-
|
1226
|
-
#
|
1227
|
-
# --output_type OUTPUT_TYPE # specify an output type i.e. json
|
1228
|
-
#
|
1229
|
-
def self.get_info(cmd, info_file, dir, options = {})
|
1230
|
-
type = options[:output_type]
|
1231
502
|
|
1232
|
-
response = `#{cmd}`
|
1233
|
-
if($?.exitstatus == 1)
|
1234
|
-
|
1235
|
-
File.delete("#{info_file}") if File.exists?(info_file)
|
1236
|
-
|
1237
|
-
if options[:output_type].nil?
|
1238
|
-
exit(1)
|
1239
|
-
else
|
1240
|
-
Zillabyte::Helpers.error("error: #{response}", type)
|
1241
|
-
end
|
1242
|
-
end
|
1243
|
-
|
1244
|
-
app_info = {}
|
1245
|
-
File.open("#{info_file}", "r+").each do |line|
|
1246
|
-
line = JSON.parse(line)
|
1247
|
-
if(line["type"])
|
1248
|
-
app_info["nodes"] << line
|
1249
|
-
else
|
1250
|
-
app_info = line
|
1251
|
-
app_info["nodes"] = []
|
1252
|
-
end
|
1253
|
-
end
|
1254
|
-
File.delete("#{info_file}")
|
1255
|
-
|
1256
|
-
app_info = app_info.to_json
|
1257
|
-
if(File.exists?("#{dir}/info_to_java.in"))
|
1258
|
-
java_pipe = open("#{dir}/info_to_java.in","w+")
|
1259
|
-
java_pipe.puts(app_info+"\n")
|
1260
|
-
java_pipe.flush
|
1261
|
-
java_pipe.close()
|
1262
|
-
end
|
1263
|
-
|
1264
|
-
app_info
|
1265
|
-
end
|
1266
503
|
|
1267
504
|
|
1268
505
|
private
|
1269
|
-
|
1270
|
-
#
|
1271
|
-
# --output_type OUTPUT_TYPE # specify an output type i.e. json
|
1272
|
-
#
|
1273
|
-
def command(arg="--execute_live", type = nil, dir = Dir.pwd, ignore_stderr = false)
|
1274
|
-
meta = Zillabyte::CLI::Config.get_config_info(dir, self, options)
|
1275
|
-
|
1276
|
-
#meta = Zillabyte::API::Functions.get_rich_meta_info_from_script(dir, self)
|
1277
|
-
error("could not extract meta information. missing zillabyte.conf.yml?", type) if meta.nil?
|
1278
|
-
error(meta["error_message"], type) if meta['status'] == "error"
|
1279
|
-
full_script = File.join(dir, meta["script"])
|
1280
|
-
stderr_opt = "2> /dev/null" if ignore_stderr
|
1281
|
-
|
1282
|
-
case meta["language"]
|
1283
|
-
when "ruby"
|
1284
|
-
# Execute in the bundler context
|
1285
|
-
cmd = "cd \"#{dir}\"; unset BUNDLE_GEMFILE; ZILLABYTE_HARNESS=1 bundle exec ruby \"#{full_script}\" #{arg} #{stderr_opt}"
|
1286
|
-
|
1287
|
-
when "python"#{
|
1288
|
-
if(File.directory?("#{dir}/vEnv"))
|
1289
|
-
cmd = "cd \"#{dir}\"; PYTHONPATH=~/zb1/multilang/python/Zillabyte #{dir}/vEnv/bin/python \"#{full_script}\" #{arg} #{stderr_opt}"
|
1290
|
-
else
|
1291
|
-
cmd = "cd \"#{dir}\"; PYTHONPATH=~/zb1/multilang/python/Zillabyte python \"#{full_script}\" #{arg} #{stderr_opt}"
|
1292
|
-
end
|
1293
|
-
|
1294
|
-
when "js"
|
1295
|
-
# cmd = "#{Zillabyte::API::CASPERJS_BIN} #{Zillabyte::API::API_CLIENT_JS} \"#{full_script}\" #{arg}"
|
1296
|
-
cmd = "cd \"#{dir}\"; NODE_PATH=~/zb1/multilang/js/src/lib #{Zillabyte::API::NODEJS_BIN} \"#{full_script}\" #{arg} #{stderr_opt}"
|
1297
|
-
|
1298
|
-
else
|
1299
|
-
error("no language specified", type)
|
1300
|
-
end
|
1301
|
-
|
1302
|
-
return cmd
|
1303
506
|
|
1304
|
-
end
|
1305
|
-
|
1306
507
|
#
|
1307
508
|
#
|
1308
509
|
#
|
@@ -1324,46 +525,4 @@ class Zillabyte::Command::Apps < Zillabyte::Command::Base
|
|
1324
525
|
|
1325
526
|
end
|
1326
527
|
|
1327
|
-
#
|
1328
|
-
#
|
1329
|
-
def read_name_from_conf(options = {})
|
1330
|
-
type = options[:output_type]
|
1331
|
-
hash = Zillabyte::API::Apps.get_rich_meta_info_from_script Dir.pwd, options
|
1332
|
-
error("No id given and current directory does not contain a valid Zillabyte configuration file. Please specify an app id or run command from the directory containing the app.",type) if hash["error"]
|
1333
|
-
hash["name"]
|
1334
|
-
end
|
1335
|
-
|
1336
|
-
def fetch_logs(hash, operation=nil, exit_on=nil)
|
1337
|
-
def color_for(operation_name)
|
1338
|
-
@color_map[operation_name] ||= @all_colors[ @color_map.size % @all_colors.size ]
|
1339
|
-
@color_map[operation_name]
|
1340
|
-
end
|
1341
|
-
|
1342
|
-
@color_map = {}
|
1343
|
-
@all_colors = [:green, :yellow, :magenta, :cyan, :light_black, :light_green, :light_yellow, :light_blue, :light_magenta, :light_cyan]
|
1344
|
-
if(hash["server"] == "localhost")
|
1345
|
-
cmd = "tail -n 500 -f /tmp/flows/f#{hash["app"]}/flow_logs/app_#{hash["app"]}.log"
|
1346
|
-
else
|
1347
|
-
cmd = "curl -G http://#{hash["app"]}:#{hash["token"]}@#{hash["server"]}:#{hash["port"]}/getLogs?app=#{hash["app"]}"
|
1348
|
-
end
|
1349
|
-
|
1350
|
-
begin
|
1351
|
-
PTY.spawn( cmd ) do |r, w, pid|
|
1352
|
-
r.each do |line|
|
1353
|
-
begin
|
1354
|
-
op = line.match(/\[(.*?)\]/)[0].to_s[1...-1]
|
1355
|
-
op_name = op.split(".")[0]
|
1356
|
-
next if op_name != operation and operation != "_ALL_"
|
1357
|
-
line_split = line.split("[")
|
1358
|
-
print line_split[0] + "[" + op.colorize(color_for(op)) + line_split[1..-1].join("[")[op.length..-1]
|
1359
|
-
break if exit_on and line.include? exit_on
|
1360
|
-
rescue Exception => e
|
1361
|
-
next
|
1362
|
-
end
|
1363
|
-
end
|
1364
|
-
end
|
1365
|
-
rescue PTY::ChildExited => e
|
1366
|
-
end
|
1367
|
-
end
|
1368
|
-
|
1369
528
|
end
|