zillabyte-cli 0.0.9
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 +15 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +29 -0
- data/bin/zillabyte +18 -0
- data/lib/zillabyte/api/base.rb +11 -0
- data/lib/zillabyte/api/data.rb +126 -0
- data/lib/zillabyte/api/flows.rb +239 -0
- data/lib/zillabyte/api/locks.rb +4 -0
- data/lib/zillabyte/api/logs.rb +32 -0
- data/lib/zillabyte/api/metrics.rb +48 -0
- data/lib/zillabyte/api/queries.rb +58 -0
- data/lib/zillabyte/api/semantic_tags.rb +24 -0
- data/lib/zillabyte/api/settings.rb +8 -0
- data/lib/zillabyte/api/sources.rb +28 -0
- data/lib/zillabyte/api/zillalogs.rb +66 -0
- data/lib/zillabyte/api.rb +170 -0
- data/lib/zillabyte/auth.rb +155 -0
- data/lib/zillabyte/cli/auth.rb +33 -0
- data/lib/zillabyte/cli/base.rb +161 -0
- data/lib/zillabyte/cli/config.rb +63 -0
- data/lib/zillabyte/cli/counters.rb +61 -0
- data/lib/zillabyte/cli/flows.rb +702 -0
- data/lib/zillabyte/cli/help.rb +137 -0
- data/lib/zillabyte/cli/helpers/data_schema_builder.rb +3 -0
- data/lib/zillabyte/cli/host.rb +21 -0
- data/lib/zillabyte/cli/logs.rb +118 -0
- data/lib/zillabyte/cli/query.rb +172 -0
- data/lib/zillabyte/cli/relations.rb +326 -0
- data/lib/zillabyte/cli/sources.rb +37 -0
- data/lib/zillabyte/cli/templates/js/simple_function.js +33 -0
- data/lib/zillabyte/cli/templates/js/zillabyte.conf.yaml +2 -0
- data/lib/zillabyte/cli/templates/python/requirements.txt +7 -0
- data/lib/zillabyte/cli/templates/python/simple_function.py +27 -0
- data/lib/zillabyte/cli/templates/python/zillabyte.conf.yaml +4 -0
- data/lib/zillabyte/cli/templates/ruby/Gemfile +3 -0
- data/lib/zillabyte/cli/templates/ruby/simple_function.rb +36 -0
- data/lib/zillabyte/cli/templates/ruby/zillabyte.conf.yaml +2 -0
- data/lib/zillabyte/cli/version.rb +11 -0
- data/lib/zillabyte/cli/zillalogs.rb +86 -0
- data/lib/zillabyte/cli.rb +37 -0
- data/lib/zillabyte/command.rb +254 -0
- data/lib/zillabyte/common/progress.rb +17 -0
- data/lib/zillabyte/common/tar.rb +102 -0
- data/lib/zillabyte/common.rb +13 -0
- data/lib/zillabyte/helpers.rb +49 -0
- data/lib/zillabyte/queries.rb +39 -0
- data/lib/zillabyte-cli/version.rb +5 -0
- data/lib/zillabyte-cli.rb +5 -0
- data/zillabyte-cli.gemspec +42 -0
- data/zillabyte_emails.csv +1 -0
- data/zillaconf.json +3 -0
- metadata +278 -0
@@ -0,0 +1,702 @@
|
|
1
|
+
require "zillabyte/cli/base"
|
2
|
+
require "zillabyte/cli/config"
|
3
|
+
require "zillabyte/common"
|
4
|
+
require "pty"
|
5
|
+
require 'indentation'
|
6
|
+
require 'open3'
|
7
|
+
require 'securerandom'
|
8
|
+
|
9
|
+
# manage custom flows
|
10
|
+
#
|
11
|
+
class Zillabyte::Command::Flows < Zillabyte::Command::Base
|
12
|
+
|
13
|
+
|
14
|
+
# flows
|
15
|
+
#
|
16
|
+
#
|
17
|
+
def index
|
18
|
+
self.list
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# flows
|
23
|
+
#
|
24
|
+
# list custom flows
|
25
|
+
#
|
26
|
+
def list
|
27
|
+
|
28
|
+
headings = ["id", "name", "state"]
|
29
|
+
rows = api.flow.list.map do |row|
|
30
|
+
if headings.size == 0
|
31
|
+
headings = row.keys
|
32
|
+
headings.delete("rel_dir")
|
33
|
+
end
|
34
|
+
v = row.values_at *headings
|
35
|
+
v
|
36
|
+
end
|
37
|
+
display "flows:\n" + Terminal::Table.new(:headings => headings, :rows => rows).to_s
|
38
|
+
display "Total number of flows: "+rows.length.to_s
|
39
|
+
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
alias_command "list", "flows:list"
|
44
|
+
|
45
|
+
|
46
|
+
def status
|
47
|
+
headings = ["name", "implementable", "implemented"]
|
48
|
+
rows = api.flows.list.map do |row|
|
49
|
+
if headings.size == 0
|
50
|
+
headings = row.keys
|
51
|
+
headings.delete("rel_dir")
|
52
|
+
end
|
53
|
+
v = row.values_at *headings
|
54
|
+
v
|
55
|
+
end
|
56
|
+
display "flow status:\n" + Terminal::Table.new(:headings => headings, :rows => rows).to_s
|
57
|
+
display "Total number of flows in queue: "+rows.length.to_s
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
alias_command "status", "flows:status"
|
62
|
+
|
63
|
+
# def status
|
64
|
+
# headings = ["name", "matches", "emits", "implementable?", "implemented", ]
|
65
|
+
# end
|
66
|
+
|
67
|
+
# flows:push [DIR]
|
68
|
+
#
|
69
|
+
# uploads a flow
|
70
|
+
#
|
71
|
+
# --config CONFIG_FILE # use the given config file
|
72
|
+
#
|
73
|
+
#Examples:
|
74
|
+
#
|
75
|
+
# $ zillabyte flows:push .
|
76
|
+
#
|
77
|
+
def push
|
78
|
+
|
79
|
+
dir = options[:directory] || shift_argument || Dir.pwd
|
80
|
+
res = api.flows.push_directory dir, progress, options
|
81
|
+
|
82
|
+
if res['error']
|
83
|
+
display "error: #{res['error_message']}"
|
84
|
+
else
|
85
|
+
display "flow ##{res['id']} #{res['action']}"
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
alias_command "exec:push", "flows:push"
|
90
|
+
alias_command "push", "flows:push"
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
# flows:pull ID DIR
|
95
|
+
#
|
96
|
+
# pulls a flow source to a directory. The target directory must be empty
|
97
|
+
#
|
98
|
+
# --force # pulls even if the directory exists
|
99
|
+
#
|
100
|
+
#Examples:
|
101
|
+
#
|
102
|
+
# $ zillabyte flows:pull .
|
103
|
+
#
|
104
|
+
def pull
|
105
|
+
|
106
|
+
id = options[:id] || shift_argument
|
107
|
+
dir = options[:directory] || shift_argument
|
108
|
+
|
109
|
+
error "no id given" if id.nil?
|
110
|
+
error "no directory given" if dir.nil?
|
111
|
+
|
112
|
+
# Create if not exists..
|
113
|
+
if File.exists?(dir)
|
114
|
+
if Dir.entries(dir).size != 2 and options[:force].nil?
|
115
|
+
error "target directory not empty. use --force to override"
|
116
|
+
end
|
117
|
+
else
|
118
|
+
FileUtils.mkdir_p(dir)
|
119
|
+
end
|
120
|
+
|
121
|
+
res = api.flows.pull_to_directory id, dir, progress
|
122
|
+
|
123
|
+
if res['error']
|
124
|
+
display "error: #{res['error_message']}"
|
125
|
+
else
|
126
|
+
display "flow ##{res['id']} pulled to #{dir}"
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
alias_command "pull_to", "flows:pull"
|
132
|
+
alias_command "pull", "flows:pull"
|
133
|
+
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
# flows:prep [DIR]
|
138
|
+
#
|
139
|
+
# prepares a flow for execution
|
140
|
+
#
|
141
|
+
def prep
|
142
|
+
|
143
|
+
dir = options[:directory] || shift_argument || Dir.pwd
|
144
|
+
meta = Zillabyte::CLI::Config.get_config_info(dir)
|
145
|
+
|
146
|
+
case meta["language"]
|
147
|
+
when "ruby"
|
148
|
+
|
149
|
+
# Execute in the bundler context
|
150
|
+
full_script = File.join(dir, meta["script"])
|
151
|
+
cmd = "cd \"#{meta['home_dir']}\"; unset BUNDLE_GEMFILE; bundle install"
|
152
|
+
exec(cmd)
|
153
|
+
|
154
|
+
when "python"
|
155
|
+
vDir = "#{meta['home_dir']}/vEnv"
|
156
|
+
lock_file = meta['home_dir']+"/zillabyte_thread_lock_file"
|
157
|
+
if File.exists?(lock_file)
|
158
|
+
sleep(1) while File.exists?(lock_file)
|
159
|
+
else
|
160
|
+
begin
|
161
|
+
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"
|
162
|
+
system cmd, :out => :out
|
163
|
+
ensure
|
164
|
+
File.delete(lock_file)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
when "js"
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
alias_command "prep", "flows:prep"
|
173
|
+
|
174
|
+
|
175
|
+
|
176
|
+
|
177
|
+
# flows:init [LANG] [DIR]
|
178
|
+
#
|
179
|
+
# initializes a new executable in DIR
|
180
|
+
# defaults to a ruby executable for the current directory
|
181
|
+
#
|
182
|
+
#Examples:
|
183
|
+
#
|
184
|
+
# $ zillabyte flows:init python contact_extractor
|
185
|
+
#
|
186
|
+
def init
|
187
|
+
|
188
|
+
|
189
|
+
lang = options[:lang] || shift_argument || "ruby"
|
190
|
+
dir = options[:dir] || shift_argument || Dir.pwd
|
191
|
+
languages = ["ruby","python", "js"]
|
192
|
+
|
193
|
+
error "Unsupported language #{lang}. We only support #{languages.join(', ')}." if not languages.include? lang
|
194
|
+
|
195
|
+
display "initializing empty #{lang} flow in #{dir}"
|
196
|
+
FileUtils.cp_r( File.expand_path("../templates/#{lang}", __FILE__) + "/." , dir )
|
197
|
+
|
198
|
+
|
199
|
+
end
|
200
|
+
alias_command "exec:init", "flows:init"
|
201
|
+
|
202
|
+
|
203
|
+
|
204
|
+
|
205
|
+
# flows:test [TEST_DATASET_ID]
|
206
|
+
#
|
207
|
+
# tests a local flow with sample data
|
208
|
+
#
|
209
|
+
# --config CONFIG_FILE # use the given config file
|
210
|
+
# --output OUTPUT_FILE # writes sink output to a file
|
211
|
+
# --wait MAX # max time to spend on each operation (default 10 seconds)
|
212
|
+
# --batches BATCHES # number of batches to emit (default 1)
|
213
|
+
#
|
214
|
+
def test
|
215
|
+
|
216
|
+
output = options[:output]
|
217
|
+
max_seconds = (options[:wait] || "30").to_i
|
218
|
+
batches = (options[:batches] || "1").to_i
|
219
|
+
|
220
|
+
def read_message(read_stream, color)
|
221
|
+
msg = nil
|
222
|
+
read_stream.each do |line|
|
223
|
+
line.strip!
|
224
|
+
if(line == "end")
|
225
|
+
return msg
|
226
|
+
end
|
227
|
+
begin
|
228
|
+
hash = JSON.parse(line)
|
229
|
+
if(hash["command"] == "done")
|
230
|
+
msg = "done"
|
231
|
+
else
|
232
|
+
msg = hash.to_json
|
233
|
+
end
|
234
|
+
rescue
|
235
|
+
next
|
236
|
+
end
|
237
|
+
end
|
238
|
+
msg
|
239
|
+
end
|
240
|
+
|
241
|
+
def write_message(write_stream, msg)
|
242
|
+
write_stream.write msg.strip + "\n"
|
243
|
+
write_stream.write "end\n"
|
244
|
+
write_stream.flush
|
245
|
+
end
|
246
|
+
|
247
|
+
def handshake(write_stream, read_stream, node, color)
|
248
|
+
begin
|
249
|
+
write_message write_stream, "{\"pidDir\": \"/tmp\"}\n"
|
250
|
+
read_message read_stream, color # Read to "end\n"
|
251
|
+
rescue Exception => e
|
252
|
+
puts "Error handshaking node: #{node}"
|
253
|
+
raise e
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
# INIT
|
259
|
+
test_data = options[:test_data] || shift_argument
|
260
|
+
dir = options[:dir] || Dir.pwd
|
261
|
+
|
262
|
+
meta = Zillabyte::API::Flows.get_rich_meta_info_from_script(dir, self, {:test => true})
|
263
|
+
if meta.nil?
|
264
|
+
error "this is not a valid zillabyte flow directory"
|
265
|
+
exit
|
266
|
+
end
|
267
|
+
|
268
|
+
# Show the user what we know about their flow...
|
269
|
+
display "inferring your flow details..."
|
270
|
+
colors = {}
|
271
|
+
describe_flow(meta, colors)
|
272
|
+
|
273
|
+
|
274
|
+
# Extract the flow's information..
|
275
|
+
nodes = meta["nodes"]
|
276
|
+
write_to_next_each = []
|
277
|
+
write_queue = []
|
278
|
+
stream_messages = {}
|
279
|
+
default_stream = "_default"
|
280
|
+
|
281
|
+
split_branches = false
|
282
|
+
|
283
|
+
# Iterate all nodes sequentially and invoke them in separate processes...
|
284
|
+
nodes.each do |node|
|
285
|
+
|
286
|
+
# Init
|
287
|
+
type = node["type"]
|
288
|
+
name = node["name"]
|
289
|
+
color = colors[name] || :default
|
290
|
+
|
291
|
+
op_display = lambda do |msg, override_color = nil|
|
292
|
+
display "#{name} - #{msg}".colorize(override_color || color)
|
293
|
+
end
|
294
|
+
|
295
|
+
# A Spout?
|
296
|
+
if type == "spout"
|
297
|
+
|
298
|
+
# A spout from relation?
|
299
|
+
if node['matches'] or node["relation"]
|
300
|
+
matches = node['matches'] || (node["relation"]["query"])
|
301
|
+
op_display.call "Grabbing remote data"
|
302
|
+
res = api.query.agnostic(matches)["rows"]
|
303
|
+
if(res.nil? or res.length == 0)
|
304
|
+
raise NameError, "Could not find data that matches your 'matches' clause"
|
305
|
+
end
|
306
|
+
res.each do |tuple|
|
307
|
+
values = {}
|
308
|
+
meta = {}
|
309
|
+
tuple.each do |k, v|
|
310
|
+
if(k == "id")
|
311
|
+
next
|
312
|
+
elsif(k == "confidence" or k == "since" or k == "source")
|
313
|
+
meta[k] = v
|
314
|
+
else
|
315
|
+
values[k] = v
|
316
|
+
end
|
317
|
+
end
|
318
|
+
read_msg = {"tuple" => values, "meta" => meta}.to_json
|
319
|
+
op_display.call "emit tuple: #{values} #{meta}"
|
320
|
+
stream_messages[default_stream] ||= []
|
321
|
+
stream_messages[default_stream] << read_msg
|
322
|
+
end
|
323
|
+
|
324
|
+
# Done processing...
|
325
|
+
next
|
326
|
+
|
327
|
+
else
|
328
|
+
|
329
|
+
# A regular spout..
|
330
|
+
stream_messages[default_stream] ||= []
|
331
|
+
batches.times do |i|
|
332
|
+
stream_messages[default_stream] << "{\"command\": \"next\"}\n"
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
336
|
+
|
337
|
+
end
|
338
|
+
|
339
|
+
|
340
|
+
|
341
|
+
# A Sink?
|
342
|
+
if type == "sink"
|
343
|
+
|
344
|
+
if split_branches || stream_messages.size > 1
|
345
|
+
sink_stream = node["consumes"]
|
346
|
+
messages = stream_messages[sink_stream] || []
|
347
|
+
else
|
348
|
+
messages = stream_messages.values.first || []
|
349
|
+
end
|
350
|
+
|
351
|
+
table = Terminal::Table.new :title => name
|
352
|
+
csv_str = CSV.generate do |csv|
|
353
|
+
header_written = false;
|
354
|
+
messages.each do |msg|
|
355
|
+
obj = JSON.parse(msg)
|
356
|
+
if obj['tuple']
|
357
|
+
if header_written == false
|
358
|
+
keys = [obj['tuple'].keys, obj['meta'].keys].flatten
|
359
|
+
csv << keys
|
360
|
+
table << keys
|
361
|
+
table << :separator
|
362
|
+
header_written = true
|
363
|
+
end
|
364
|
+
vals = [obj['tuple'].values, obj['meta'].values].flatten
|
365
|
+
csv << vals
|
366
|
+
table << vals
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
display table.to_s.colorize(color)
|
372
|
+
|
373
|
+
if output
|
374
|
+
filename = "#{output}.csv"
|
375
|
+
f = File.open(filename, "w")
|
376
|
+
f.write(csv_str)
|
377
|
+
f.close()
|
378
|
+
op_display.call "output written to #{filename}"
|
379
|
+
end
|
380
|
+
|
381
|
+
|
382
|
+
|
383
|
+
next
|
384
|
+
end
|
385
|
+
|
386
|
+
|
387
|
+
cmd = command("--execute_live --name #{name}")
|
388
|
+
begin
|
389
|
+
|
390
|
+
# Start the operation...
|
391
|
+
op_display.call "beginning #{type} #{name}"
|
392
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thread|
|
393
|
+
begin
|
394
|
+
|
395
|
+
# Init
|
396
|
+
handshake stdin, stdout, node, color
|
397
|
+
write_queue = []
|
398
|
+
read_queue = []
|
399
|
+
|
400
|
+
# Get the incoming stream
|
401
|
+
if !split_branches && stream_messages.size == 1 # i.e. the number of streams we're dealing with right now
|
402
|
+
# Assume default stream
|
403
|
+
stream_name = stream_messages.keys.first
|
404
|
+
write_queue = stream_messages.values.first.clone
|
405
|
+
stream_messages.delete(stream_name)
|
406
|
+
else
|
407
|
+
# Multiple streams...
|
408
|
+
split_branches = true;
|
409
|
+
if node['consumes'].nil?
|
410
|
+
error "The node #{name} must declare which stream it 'consumes'"
|
411
|
+
end
|
412
|
+
stream_name = node["consumes"]
|
413
|
+
write_queue = stream_messages[stream_name].clone()
|
414
|
+
stream_messages.delete(stream_name)
|
415
|
+
end
|
416
|
+
|
417
|
+
# Start writing the messages...
|
418
|
+
stuff_to_read = false
|
419
|
+
writing_thread = Thread.start do
|
420
|
+
until(write_queue.empty?)
|
421
|
+
|
422
|
+
# Make sure we're not reading anything...
|
423
|
+
while(stuff_to_read) # TODO: semaphores
|
424
|
+
sleep 0.5 # spin wait
|
425
|
+
end
|
426
|
+
|
427
|
+
# Get next mesage
|
428
|
+
write_msg = write_queue.shift
|
429
|
+
|
430
|
+
# Make it human-understable
|
431
|
+
write_json = JSON.parse(write_msg)
|
432
|
+
if write_json['tuple']
|
433
|
+
op_display.call "receiving: #{write_json['tuple']}"
|
434
|
+
elsif write_json['command'] == 'next'
|
435
|
+
op_display.call "starting next spout batch"
|
436
|
+
else
|
437
|
+
puts write_json
|
438
|
+
end
|
439
|
+
|
440
|
+
# Actually send it to the process
|
441
|
+
begin
|
442
|
+
write_message stdin, write_msg
|
443
|
+
stuff_to_read = true
|
444
|
+
sleep 0.1
|
445
|
+
rescue Exception => e
|
446
|
+
puts "Error running #{cmd}: #{e}"
|
447
|
+
raise e
|
448
|
+
end
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
# Start reading messages...
|
453
|
+
reading_thread = Thread.start do
|
454
|
+
while(true)
|
455
|
+
|
456
|
+
# Get next message
|
457
|
+
read_msg = read_message(stdout, color)
|
458
|
+
if read_msg == "done" || read_msg.nil?
|
459
|
+
stuff_to_read = false
|
460
|
+
if write_queue.empty?
|
461
|
+
break # exit while loop
|
462
|
+
else
|
463
|
+
sleep 0.5 # spin wait
|
464
|
+
next
|
465
|
+
end
|
466
|
+
end
|
467
|
+
stuff_to_read = true
|
468
|
+
|
469
|
+
# Process message
|
470
|
+
obj = JSON.parse(read_msg)
|
471
|
+
if obj['tuple']
|
472
|
+
|
473
|
+
# Conver to a incoming tuple for the next operation
|
474
|
+
next_msg = {
|
475
|
+
:tuple => obj['tuple'],
|
476
|
+
:meta => obj['meta']
|
477
|
+
}
|
478
|
+
emit_stream = obj['stream'] || default_stream
|
479
|
+
stream_messages[emit_stream] ||= []
|
480
|
+
stream_messages[emit_stream] << next_msg.to_json
|
481
|
+
op_display.call "emitted: #{obj['tuple']} to #{emit_stream}"
|
482
|
+
elsif obj['command'] == 'log'
|
483
|
+
op_display.call "log: #{obj['msg']}"
|
484
|
+
else
|
485
|
+
error "unknown message: #{read_msg}"
|
486
|
+
end
|
487
|
+
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
|
492
|
+
# stderr thread
|
493
|
+
stderr_thread = Thread.start do
|
494
|
+
stderr.each do |line|
|
495
|
+
op_display.call("stderr: #{line}", :red)
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
begin
|
500
|
+
killed = Timeout.timeout(max_seconds) do
|
501
|
+
reading_thread.join()
|
502
|
+
writing_thread.join()
|
503
|
+
stderr_thread.kill()
|
504
|
+
op_display.call "completed #{type} #{name}"
|
505
|
+
end
|
506
|
+
rescue Timeout::Error
|
507
|
+
op_display.call "max time reached. preempting #{type} #{name}. set --wait to increase", :red
|
508
|
+
reading_thread.kill() if reading_thread.alive?
|
509
|
+
writing_thread.kill() if writing_thread.alive?
|
510
|
+
stderr_thread.kill() if stderr_thread.alive?
|
511
|
+
end
|
512
|
+
|
513
|
+
rescue Errno::EIO
|
514
|
+
puts "Errno:EIO error, but this probably just means " +
|
515
|
+
"that the process has finished giving output"
|
516
|
+
end
|
517
|
+
end
|
518
|
+
rescue PTY::ChildExited
|
519
|
+
puts "The child process exited!"
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
end
|
524
|
+
alias_command "test", "flows:test"
|
525
|
+
|
526
|
+
|
527
|
+
|
528
|
+
|
529
|
+
|
530
|
+
# flows:status ID
|
531
|
+
#
|
532
|
+
# gets the flows status
|
533
|
+
#
|
534
|
+
# --config CONFIG_FILE # use the given config file
|
535
|
+
#
|
536
|
+
def status
|
537
|
+
|
538
|
+
id = options[:id] || shift_argument
|
539
|
+
|
540
|
+
display api.flows.get(id)
|
541
|
+
|
542
|
+
end
|
543
|
+
|
544
|
+
|
545
|
+
|
546
|
+
|
547
|
+
|
548
|
+
|
549
|
+
# flows:kill ID
|
550
|
+
#
|
551
|
+
# kills the given flow
|
552
|
+
#
|
553
|
+
# --config CONFIG_FILE # use the given config file
|
554
|
+
#
|
555
|
+
def kill
|
556
|
+
|
557
|
+
id = options[:id] || shift_argument
|
558
|
+
api.flows.kill(id)
|
559
|
+
display "flow #{id} killed"
|
560
|
+
|
561
|
+
end
|
562
|
+
|
563
|
+
|
564
|
+
|
565
|
+
|
566
|
+
|
567
|
+
|
568
|
+
# flows:live_run [DIR]
|
569
|
+
#
|
570
|
+
# runs a local flow with live data
|
571
|
+
#
|
572
|
+
# --config CONFIG_FILE # use the given config file
|
573
|
+
#
|
574
|
+
def live_run
|
575
|
+
|
576
|
+
name = options[:name] || shift_argument
|
577
|
+
thread_id = options[:thread] || shift_argument || ""
|
578
|
+
dir = options[:directory] || shift_argument || Dir.pwd
|
579
|
+
meta = Zillabyte::CLI::Config.get_config_info(dir)
|
580
|
+
|
581
|
+
if meta.nil?
|
582
|
+
throw "could not find meta information for: #{dir}"
|
583
|
+
end
|
584
|
+
|
585
|
+
if(thread_id == "")
|
586
|
+
exec(command("--execute_live --name #{name.to_s}"))
|
587
|
+
else
|
588
|
+
exec(command("--execute_live --name #{name.to_s} --pipe #{thread_id}"))
|
589
|
+
end
|
590
|
+
end
|
591
|
+
alias_command "live_run", "flows:live_run"
|
592
|
+
|
593
|
+
|
594
|
+
# flows:info [DIR]
|
595
|
+
#
|
596
|
+
# outputs the info for the flow in the dir.
|
597
|
+
#
|
598
|
+
# --pretty # Pretty prints the info output
|
599
|
+
#
|
600
|
+
def info
|
601
|
+
info_file = SecureRandom.uuid
|
602
|
+
cmd = command("--info --file #{info_file}")
|
603
|
+
flow_info = Zillabyte::Command::Flows.get_info(cmd, info_file)
|
604
|
+
|
605
|
+
if options[:pretty]
|
606
|
+
puts JSON.pretty_generate(JSON.parse(flow_info))
|
607
|
+
else
|
608
|
+
puts flow_info
|
609
|
+
end
|
610
|
+
exit
|
611
|
+
end
|
612
|
+
alias_command "info", "flows:info"
|
613
|
+
|
614
|
+
def self.get_info(cmd, info_file, options = {})
|
615
|
+
response = `#{cmd}`
|
616
|
+
if($?.exitstatus == 1)
|
617
|
+
File.delete("#{info_file}") if File.exists?(info_file)
|
618
|
+
puts "error: #{response}" if options[:test]
|
619
|
+
Process.exit 1
|
620
|
+
end
|
621
|
+
|
622
|
+
flow_info = {}
|
623
|
+
File.open("#{info_file}", "r+").each do |line|
|
624
|
+
line = JSON.parse(line)
|
625
|
+
if(line["type"])
|
626
|
+
flow_info["nodes"] << line
|
627
|
+
else
|
628
|
+
flow_info = line
|
629
|
+
flow_info["nodes"] = []
|
630
|
+
end
|
631
|
+
end
|
632
|
+
File.delete("#{info_file}")
|
633
|
+
|
634
|
+
flow_info = flow_info.to_json
|
635
|
+
if(File.exists?("info_to_java.in"))
|
636
|
+
java_pipe = open("info_to_java.in","w+")
|
637
|
+
java_pipe.puts(flow_info+"\n")
|
638
|
+
java_pipe.flush
|
639
|
+
java_pipe.close()
|
640
|
+
end
|
641
|
+
|
642
|
+
flow_info
|
643
|
+
end
|
644
|
+
|
645
|
+
private
|
646
|
+
|
647
|
+
|
648
|
+
def command(arg="--execute_live", ignore_stderr = false)
|
649
|
+
|
650
|
+
dir = options[:directory] || shift_argument || Dir.pwd
|
651
|
+
meta = Zillabyte::CLI::Config.get_config_info(dir, progress=nil, options)
|
652
|
+
#meta = Zillabyte::API::Functions.get_rich_meta_info_from_script(dir, self)
|
653
|
+
error "could not extract meta information. missing zillabyte.conf.yml?" if meta.nil?
|
654
|
+
error meta["error_message"] if meta['status'] == "error"
|
655
|
+
full_script = File.join(dir, meta["script"])
|
656
|
+
stderr_opt = "2> /dev/null" if ignore_stderr
|
657
|
+
|
658
|
+
case meta["language"]
|
659
|
+
when "ruby"
|
660
|
+
# Execute in the bundler context
|
661
|
+
cmd = "cd \"#{dir}\"; unset BUNDLE_GEMFILE; ZILLABYTE_HARNESS=1 bundle exec ruby \"#{full_script}\" #{arg} #{stderr_opt}"
|
662
|
+
|
663
|
+
when "python"#{
|
664
|
+
if(File.directory?("#{dir}/vEnv"))
|
665
|
+
cmd = "cd \"#{dir}\"; PYTHONPATH=~/zb1/multilang/python/Zillabyte #{dir}/vEnv/bin/python \"#{full_script}\" #{arg} #{stderr_opt}"
|
666
|
+
else
|
667
|
+
cmd = "cd \"#{dir}\"; PYTHONPATH=~/zb1/multilang/python/Zillabyte python \"#{full_script}\" #{arg} #{stderr_opt}"
|
668
|
+
end
|
669
|
+
|
670
|
+
when "js"
|
671
|
+
# cmd = "#{Zillabyte::API::CASPERJS_BIN} #{Zillabyte::API::API_CLIENT_JS} \"#{full_script}\" #{arg}"
|
672
|
+
cmd = "cd \"#{dir}\"; NODE_PATH=~/zb1/multilang/js #{Zillabyte::API::NODEJS_BIN} \"#{full_script}\" #{arg} #{stderr_opt}"
|
673
|
+
|
674
|
+
else
|
675
|
+
error "no language specified"
|
676
|
+
end
|
677
|
+
|
678
|
+
return cmd
|
679
|
+
|
680
|
+
end
|
681
|
+
|
682
|
+
|
683
|
+
def describe_flow(meta, colors = {})
|
684
|
+
@colors ||= [:green, :yellow, :magenta, :cyan, :light_black, :light_green, :light_yellow, :light_blue, :light_magenta, :light_cyan]
|
685
|
+
rjust = 20
|
686
|
+
display "#{'flow name'.rjust(rjust)}: #{meta['name']}"
|
687
|
+
display "#{'flow language'.rjust(rjust)}: #{meta['language']}"
|
688
|
+
|
689
|
+
meta['nodes'].each_with_index do |node, index|
|
690
|
+
colors[node['name']] ||= @colors.shift
|
691
|
+
color = colors[node['name']]
|
692
|
+
display (("="*rjust + " operation ##{index}").colorize(color))
|
693
|
+
display "#{"name".rjust(rjust)}: #{node['name'].to_s.colorize(color)}"
|
694
|
+
display "#{"type".rjust(rjust)}: #{node['type'].to_s.colorize(color)}"
|
695
|
+
display "#{"matches".rjust(rjust)}: #{JSON.pretty_generate(node['matches']).indent(rjust+2).lstrip.colorize(color)}" if node['matches']
|
696
|
+
display "#{"emits".rjust(rjust)}: #{JSON.pretty_generate(node['emits']).indent(rjust+2).lstrip.colorize(color)}" if node['emits']
|
697
|
+
end
|
698
|
+
|
699
|
+
end
|
700
|
+
|
701
|
+
|
702
|
+
end
|