td 0.12.0 → 0.13.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 +4 -4
- data/.travis.yml +5 -0
- data/ChangeLog +15 -0
- data/lib/td/command/common.rb +96 -26
- data/lib/td/command/connector.rb +58 -36
- data/lib/td/command/import.rb +162 -1
- data/lib/td/command/job.rb +33 -23
- data/lib/td/command/list.rb +3 -2
- data/lib/td/command/query.rb +3 -5
- data/lib/td/command/runner.rb +1 -2
- data/lib/td/command/table.rb +29 -18
- data/lib/td/compact_format_yamler.rb +41 -0
- data/lib/td/connector_config_normalizer.rb +32 -0
- data/lib/td/version.rb +1 -1
- data/spec/spec_helper.rb +18 -0
- data/spec/td/command/connector_spec.rb +153 -0
- data/spec/td/command/import_spec.rb +179 -0
- data/spec/td/command/job_spec.rb +1 -1
- data/spec/td/command/table_spec.rb +113 -0
- data/spec/td/common_spec.rb +23 -1
- data/spec/td/compact_format_yamler_spec.rb +38 -0
- data/spec/td/connector_config_normalizer_spec.rb +62 -0
- data/td.gemspec +2 -1
- metadata +25 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7b9e3d57fd08024e999160fe1678031a293941e
|
4
|
+
data.tar.gz: 39454d414e3afddb9e83fcf3e3c574c98911536f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4846a9e5e75112f0350cbb4824e5335cb2861fabc927ddcacefe81a9a4894af08da0903920db5d1b4fcfd170b571341e5b41a77293c7a572df92ca4eec487e1
|
7
|
+
data.tar.gz: 0909c9942d35536d644db19f3ad0c3bf219d78ad59388ef8cee92af5c7f0d3271c4779503d5268dda0bd65cc2bcbaf231c3d5f02369b43dbcef0af50f77c364b
|
data/.travis.yml
CHANGED
data/ChangeLog
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
== 2015-10-29 version 0.13.0
|
2
|
+
|
3
|
+
* fix command error when recive 40X errror from server
|
4
|
+
* support show config_diff in connector:[list,show,create,delete]
|
5
|
+
* add new command `table:rename`. This command can rename table.
|
6
|
+
* fix connector:history crash if history has queueing state.
|
7
|
+
* fix connector:run time argument bug.
|
8
|
+
* support query, job:show write Hash format JSON. If you use it, you set --column-header and -f json options.
|
9
|
+
* change default file name create by connector:guess rename to `config.yml`.
|
10
|
+
* remove --exclude option from connector:issue
|
11
|
+
* support --g option for connector:config. It can specified guess plugin.
|
12
|
+
* improve progressbar
|
13
|
+
* support filter section for config file.
|
14
|
+
* remove --primary-key option from table:create
|
15
|
+
|
1
16
|
== 2015-08-10 version 0.12.0
|
2
17
|
|
3
18
|
* Improve error message from APIError
|
data/lib/td/command/common.rb
CHANGED
@@ -247,6 +247,13 @@ EOS
|
|
247
247
|
table
|
248
248
|
end
|
249
249
|
|
250
|
+
def table_exist?(database, table_name)
|
251
|
+
database.table(table_name)
|
252
|
+
true
|
253
|
+
rescue
|
254
|
+
false
|
255
|
+
end
|
256
|
+
|
250
257
|
def create_database_and_table_if_not_exist(client, db_name, table_name)
|
251
258
|
# Merge with db_create and table_create after refactoring
|
252
259
|
API.validate_database_name(db_name)
|
@@ -295,6 +302,22 @@ EOS
|
|
295
302
|
end
|
296
303
|
end
|
297
304
|
|
305
|
+
def puts_with_indent(message, indent_size = 4, io = $stdout)
|
306
|
+
io.puts "#{' ' * indent_size}#{message}"
|
307
|
+
end
|
308
|
+
|
309
|
+
def create_file_backup(out)
|
310
|
+
return unless File.exist?(out)
|
311
|
+
0.upto(100) do |idx|
|
312
|
+
backup = "#{out}.#{idx}"
|
313
|
+
unless File.exist?(backup)
|
314
|
+
FileUtils.mv(out, backup)
|
315
|
+
return
|
316
|
+
end
|
317
|
+
end
|
318
|
+
raise "backup file creation failed"
|
319
|
+
end
|
320
|
+
|
298
321
|
def self.validate_api_endpoint(endpoint)
|
299
322
|
require 'uri'
|
300
323
|
|
@@ -351,26 +374,28 @@ EOS
|
|
351
374
|
files
|
352
375
|
end
|
353
376
|
|
354
|
-
class
|
355
|
-
def initialize(msg)
|
356
|
-
@base_msg = msg
|
357
|
-
$stdout.print @base_msg + " " * 10
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
class TimeBasedDownloadProgressIndicator < DownloadProgressIndicator
|
377
|
+
class TimeBasedDownloadProgressIndicator
|
362
378
|
def initialize(msg, start_time, periodicity = 2)
|
379
|
+
require 'ruby-progressbar'
|
380
|
+
|
363
381
|
@start_time = start_time
|
364
382
|
@last_time = start_time
|
365
383
|
@periodicity = periodicity
|
366
|
-
|
384
|
+
|
385
|
+
@progress_bar = ProgressBar.create(
|
386
|
+
title: msg,
|
387
|
+
total: nil,
|
388
|
+
format: formated_with_elasped_time(0),
|
389
|
+
output: $stdout,
|
390
|
+
unknown_progress_animation_steps: [' '],
|
391
|
+
)
|
392
|
+
|
393
|
+
update_progress_bar(formated_with_elasped_time(Command.humanize_elapsed_time(@start_time, @start_time)))
|
367
394
|
end
|
368
395
|
|
369
396
|
def update
|
370
397
|
if (time = Time.now.to_i) - @last_time >= @periodicity
|
371
|
-
|
372
|
-
$stdout.print "\r" + " " * (msg.length + 10)
|
373
|
-
$stdout.print msg
|
398
|
+
update_progress_bar(formated_with_elasped_time(Command.humanize_elapsed_time(@start_time, time)))
|
374
399
|
@last_time = time
|
375
400
|
true
|
376
401
|
else
|
@@ -379,13 +404,37 @@ EOS
|
|
379
404
|
end
|
380
405
|
|
381
406
|
def finish
|
382
|
-
|
407
|
+
# NOTE %B is for clear terminal line
|
408
|
+
update_progress_bar("%t: done %B")
|
409
|
+
end
|
410
|
+
|
411
|
+
private
|
412
|
+
|
413
|
+
def formated_with_elasped_time(elasped_time)
|
414
|
+
# NOTE %B is for clear terminal line
|
415
|
+
"%t: #{elasped_time} elapsed %B"
|
416
|
+
end
|
417
|
+
|
418
|
+
def update_progress_bar(format)
|
419
|
+
@progress_bar.format = format
|
420
|
+
@progress_bar.refresh
|
383
421
|
end
|
384
422
|
end
|
385
423
|
|
386
|
-
class SizeBasedDownloadProgressIndicator
|
387
|
-
def initialize(msg,
|
388
|
-
|
424
|
+
class SizeBasedDownloadProgressIndicator
|
425
|
+
def initialize(msg, total_size, perc_step = 1, min_periodicity = nil)
|
426
|
+
require 'ruby-progressbar'
|
427
|
+
|
428
|
+
@total_size = total_size
|
429
|
+
@total_byte_size = Command.humanize_bytesize(@total_size) unless unknown_progress_mode?
|
430
|
+
|
431
|
+
@progress_bar = ProgressBar.create(
|
432
|
+
title: msg,
|
433
|
+
total: unknown_progress_mode? ? nil : @total_size,
|
434
|
+
format: formated_title(0),
|
435
|
+
output: $stdout,
|
436
|
+
)
|
437
|
+
@progress_bar.refresh
|
389
438
|
|
390
439
|
# perc_step is how small a percentage increment can be shown
|
391
440
|
@perc_step = perc_step
|
@@ -397,22 +446,18 @@ EOS
|
|
397
446
|
# track progress
|
398
447
|
@last_perc_step = 0
|
399
448
|
@last_time = @start_time
|
400
|
-
|
401
|
-
super(msg)
|
402
449
|
end
|
403
450
|
|
404
451
|
def update(curr_size)
|
405
|
-
if
|
406
|
-
|
407
|
-
$stdout.print msg
|
452
|
+
if unknown_progress_mode?
|
453
|
+
update_progress_bar(curr_size)
|
408
454
|
true
|
409
455
|
else
|
410
|
-
ratio = (curr_size.to_f * 100 / @
|
456
|
+
ratio = (curr_size.to_f * 100 / @total_size).round(1)
|
411
457
|
if ratio >= (@last_perc_step + @perc_step) &&
|
412
458
|
(!@min_periodicity || (time = Time.now.to_i) - @last_time >= @min_periodicity)
|
413
|
-
|
414
|
-
|
415
|
-
$stdout.print msg
|
459
|
+
update_progress_bar(curr_size)
|
460
|
+
|
416
461
|
@last_perc_step = ratio
|
417
462
|
@last_time = time
|
418
463
|
true
|
@@ -423,7 +468,32 @@ EOS
|
|
423
468
|
end
|
424
469
|
|
425
470
|
def finish
|
426
|
-
|
471
|
+
if unknown_progress_mode?
|
472
|
+
@progress_bar.format = "%t : #{Command.humanize_bytesize(@progress_bar.progress).rjust(10)} Done"
|
473
|
+
@progress_bar.progress = 0
|
474
|
+
else
|
475
|
+
update_progress_bar(@progress_bar.total)
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
private
|
480
|
+
|
481
|
+
def unknown_progress_mode?
|
482
|
+
@total_size.nil? || @total_size == 0
|
483
|
+
end
|
484
|
+
|
485
|
+
def update_progress_bar(curr_size)
|
486
|
+
@progress_bar.format = formated_title(curr_size)
|
487
|
+
@progress_bar.progress = curr_size
|
488
|
+
end
|
489
|
+
|
490
|
+
def formated_title(curr_size)
|
491
|
+
if unknown_progress_mode?
|
492
|
+
"%t : #{Command.humanize_bytesize(curr_size).rjust(10)}"
|
493
|
+
else
|
494
|
+
rjust_size = @total_byte_size.size + 1
|
495
|
+
"%t #{Command.humanize_bytesize(curr_size).rjust(rjust_size)} / #{@total_byte_size.rjust(rjust_size)} : %w "
|
496
|
+
end
|
427
497
|
end
|
428
498
|
end
|
429
499
|
|
data/lib/td/command/connector.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'td/command/common'
|
2
2
|
require 'td/command/job'
|
3
|
+
require 'td/connector_config_normalizer'
|
3
4
|
require 'json'
|
4
5
|
require 'uri'
|
5
6
|
require 'yaml'
|
7
|
+
require 'time'
|
6
8
|
|
7
9
|
module TreasureData
|
8
10
|
module Command
|
@@ -16,13 +18,17 @@ module Command
|
|
16
18
|
def connector_guess(op)
|
17
19
|
type = 's3'
|
18
20
|
id = secret = source = nil
|
19
|
-
out = '
|
21
|
+
out = 'config.yml'
|
22
|
+
guess_plugins = {}
|
20
23
|
|
21
24
|
op.on('--type[=TYPE]', "(obsoleted)") { |s| type = s }
|
22
25
|
op.on('--access-id ID', "(obsoleted)") { |s| id = s }
|
23
26
|
op.on('--access-secret SECRET', "(obsoleted)") { |s| secret = s }
|
24
27
|
op.on('--source SOURCE', "(obsoleted)") { |s| source = s }
|
25
28
|
op.on('-o', '--out FILE_NAME', "output file name for connector:preview") { |s| out = s }
|
29
|
+
op.on('-g', '--guess NAME,NAME,...', 'specify list of guess plugins that users want to use') {|s|
|
30
|
+
guess_plugins['guess_plugins'] = s.split(',')
|
31
|
+
}
|
26
32
|
|
27
33
|
config_file = op.cmd_parse
|
28
34
|
if config_file
|
@@ -30,6 +36,7 @@ module Command
|
|
30
36
|
out ||= config_file
|
31
37
|
else
|
32
38
|
begin
|
39
|
+
$stdout.puts 'Command line option is obsoleted. You should use configuration file.'
|
33
40
|
required('--access-id', id)
|
34
41
|
required('--access-secret', secret)
|
35
42
|
required('--source', source)
|
@@ -60,14 +67,17 @@ module Command
|
|
60
67
|
}
|
61
68
|
end
|
62
69
|
|
70
|
+
config = TreasureData::ConnectorConfigNormalizer.new(config).normalized_config
|
71
|
+
config['exec'].merge!(guess_plugins)
|
72
|
+
|
63
73
|
client = get_client
|
64
74
|
job = client.bulk_load_guess(config: config)
|
65
75
|
|
66
|
-
|
76
|
+
create_file_backup(out)
|
67
77
|
if /\.json\z/ =~ out
|
68
78
|
config_str = JSON.pretty_generate(job['config'])
|
69
79
|
else
|
70
|
-
config_str =
|
80
|
+
config_str = config_to_yaml(job['config'])
|
71
81
|
end
|
72
82
|
File.open(out, 'w') do |f|
|
73
83
|
f << config_str
|
@@ -110,14 +120,13 @@ module Command
|
|
110
120
|
def connector_issue(op)
|
111
121
|
database = table = nil
|
112
122
|
time_column = nil
|
113
|
-
wait
|
123
|
+
wait = false
|
114
124
|
auto_create = false
|
115
125
|
|
116
126
|
op.on('--database DB_NAME', "destination database") { |s| database = s }
|
117
127
|
op.on('--table TABLE_NAME', "destination table") { |s| table = s }
|
118
128
|
op.on('--time-column COLUMN_NAME', "data partitioning key") { |s| time_column = s } # unnecessary but for backward compatibility
|
119
129
|
op.on('-w', '--wait', 'wait for finishing the job', TrueClass) { |b| wait = b }
|
120
|
-
op.on('-x', '--exclude', 'do not automatically retrieve the job result', TrueClass) { |b| exclude = b }
|
121
130
|
op.on('--auto-create-table', "Create table and database if doesn't exist", TrueClass) { |b|
|
122
131
|
auto_create = b
|
123
132
|
}
|
@@ -142,7 +151,7 @@ module Command
|
|
142
151
|
$stdout.puts "Use '#{$prog} " + Config.cl_options_string + "job:show #{job_id}' to show the status."
|
143
152
|
|
144
153
|
if wait
|
145
|
-
wait_connector_job(client, job_id
|
154
|
+
wait_connector_job(client, job_id)
|
146
155
|
end
|
147
156
|
end
|
148
157
|
|
@@ -152,7 +161,7 @@ module Command
|
|
152
161
|
|
153
162
|
client = get_client()
|
154
163
|
# TODO database and table is empty at present. Fix API or Client.
|
155
|
-
keys = ['name', 'cron', 'timezone', 'delay', 'database', 'table', 'config']
|
164
|
+
keys = ['name', 'cron', 'timezone', 'delay', 'database', 'table', 'config', 'config_diff']
|
156
165
|
fields = keys.map { |e| e.capitalize.to_sym }
|
157
166
|
rows = client.bulk_load_list().sort_by { |e|
|
158
167
|
e['name']
|
@@ -229,35 +238,47 @@ module Command
|
|
229
238
|
fields = [:JobID, :Status, :Records, :Database, :Table, :Priority, :Started, :Duration]
|
230
239
|
client = get_client()
|
231
240
|
rows = client.bulk_load_history(name).map { |e|
|
241
|
+
time_property = if e['start_at']
|
242
|
+
{
|
243
|
+
:Started => Time.at(e['start_at']),
|
244
|
+
:Duration => (e['end_at'].nil? ? Time.now.to_i : e['end_at']) - e['start_at'],
|
245
|
+
}
|
246
|
+
else
|
247
|
+
{:Started => '', :Duration => ''}
|
248
|
+
end
|
249
|
+
|
232
250
|
{
|
233
251
|
:JobID => e['job_id'],
|
234
252
|
:Status => e['status'],
|
235
253
|
:Records => e['records'],
|
236
254
|
# TODO: td-client-ruby should retuan only name
|
237
|
-
:Database => e['database']['name'],
|
238
|
-
:Table
|
255
|
+
:Database => e['database'] ? e['database']['name'] : '',
|
256
|
+
:Table => e['table'] ? e['table']['name'] : '',
|
239
257
|
:Priority => e['priority'],
|
240
|
-
|
241
|
-
:Duration => (e['end_at'].nil? ? Time.now.to_i : e['end_at']) - e['start_at'],
|
242
|
-
}
|
258
|
+
}.merge(time_property)
|
243
259
|
}
|
244
260
|
$stdout.puts cmd_render_table(rows, :fields => fields, :render_format => op.render_format)
|
245
261
|
end
|
246
262
|
|
247
263
|
def connector_run(op)
|
248
|
-
wait =
|
264
|
+
wait = false
|
249
265
|
op.on('-w', '--wait', 'wait for finishing the job', TrueClass) { |b| wait = b }
|
250
|
-
op.on('-x', '--exclude', 'do not automatically retrieve the job result', TrueClass) { |b| exclude = b }
|
251
266
|
|
252
267
|
name, scheduled_time = op.cmd_parse
|
268
|
+
time = if scheduled_time
|
269
|
+
Time.parse(scheduled_time).to_i
|
270
|
+
else
|
271
|
+
current_time.to_i
|
272
|
+
end
|
253
273
|
|
254
274
|
client = get_client()
|
255
|
-
job_id = client.bulk_load_run(name)
|
275
|
+
job_id = client.bulk_load_run(name, time)
|
276
|
+
|
256
277
|
$stdout.puts "Job #{job_id} is queued."
|
257
278
|
$stdout.puts "Use '#{$prog} " + Config.cl_options_string + "job:show #{job_id}' to show the status."
|
258
279
|
|
259
280
|
if wait
|
260
|
-
wait_connector_job(client, job_id
|
281
|
+
wait_connector_job(client, job_id)
|
261
282
|
end
|
262
283
|
end
|
263
284
|
|
@@ -277,6 +298,19 @@ private
|
|
277
298
|
nil
|
278
299
|
end
|
279
300
|
|
301
|
+
def config_to_yaml(config)
|
302
|
+
config_str = ''
|
303
|
+
begin
|
304
|
+
require 'td/compact_format_yamler'
|
305
|
+
config_str = TreasureData::CompactFormatYamler.dump(config)
|
306
|
+
rescue
|
307
|
+
# NOTE fail back
|
308
|
+
config_str = YAML.dump(config)
|
309
|
+
end
|
310
|
+
config_str
|
311
|
+
end
|
312
|
+
|
313
|
+
|
280
314
|
def prepare_bulkload_job_config(config_file)
|
281
315
|
unless File.exist?(config_file)
|
282
316
|
raise ParameterConfigurationError, "configuration file: #{config_file} not found"
|
@@ -293,25 +327,7 @@ private
|
|
293
327
|
raise ParameterConfigurationError, "configuration file: #{config_file} #{e.message}"
|
294
328
|
end
|
295
329
|
|
296
|
-
|
297
|
-
if config.size != 1
|
298
|
-
raise "Setting #{(config.keys - ['config']).inspect} keys in a configuration file is not supported. Please set options to the command line argument."
|
299
|
-
end
|
300
|
-
config = config['config']
|
301
|
-
end
|
302
|
-
config
|
303
|
-
end
|
304
|
-
|
305
|
-
def create_bulkload_job_file_backup(out)
|
306
|
-
return unless File.exist?(out)
|
307
|
-
0.upto(100) do |idx|
|
308
|
-
backup = "#{out}.#{idx}"
|
309
|
-
unless File.exist?(backup)
|
310
|
-
FileUtils.mv(out, backup)
|
311
|
-
return
|
312
|
-
end
|
313
|
-
end
|
314
|
-
raise "backup file creation failed"
|
330
|
+
TreasureData::ConnectorConfigNormalizer.new(config).normalized_config
|
315
331
|
end
|
316
332
|
|
317
333
|
def dump_connector_session(session)
|
@@ -323,13 +339,19 @@ private
|
|
323
339
|
$stdout.puts "Table : #{session["table"]}"
|
324
340
|
$stdout.puts "Config"
|
325
341
|
$stdout.puts YAML.dump(session["config"])
|
342
|
+
$stdout.puts
|
343
|
+
$stdout.puts "Config Diff"
|
344
|
+
$stdout.puts YAML.dump(session["config_diff"])
|
326
345
|
end
|
327
346
|
|
328
|
-
def wait_connector_job(client, job_id
|
347
|
+
def wait_connector_job(client, job_id)
|
329
348
|
job = client.job(job_id)
|
330
349
|
wait_job(job, true)
|
331
350
|
$stdout.puts "Status : #{job.status}"
|
332
351
|
end
|
333
352
|
|
353
|
+
def current_time
|
354
|
+
Time.now
|
355
|
+
end
|
334
356
|
end
|
335
357
|
end
|
data/lib/td/command/import.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'td/updater'
|
2
2
|
require 'time'
|
3
|
+
require 'yaml'
|
3
4
|
|
4
5
|
module TreasureData
|
5
6
|
module Command
|
@@ -77,6 +78,50 @@ module Command
|
|
77
78
|
bulk_import_unfreeze(op)
|
78
79
|
end
|
79
80
|
|
81
|
+
def import_config(op)
|
82
|
+
out = 'td-bulkload.yml'
|
83
|
+
options = {
|
84
|
+
'format' => 'csv'
|
85
|
+
}
|
86
|
+
not_migrate_options = []
|
87
|
+
op.on('-o', '--out FILE_NAME', "output file name for connector:guess") { |s| out = s }
|
88
|
+
op.on('-f', '--format FORMAT', "source file format [csv, tsv, mysql]; default=csv") { |s| options['format'] = s }
|
89
|
+
|
90
|
+
op.on('--db-url URL', "Database Connection URL") { |s| options['db_url'] = s }
|
91
|
+
op.on('--db-user NAME', "user name for database") { |s| options['db_user'] = s }
|
92
|
+
op.on('--db-password PASSWORD', "password for database") { |s| options['db_password'] = s }
|
93
|
+
%w(--columns --column-header --time-column --time-format).each do |not_migrate_option|
|
94
|
+
opt_arg_name = not_migrate_option.gsub('--', '').upcase
|
95
|
+
op.on("#{not_migrate_option} #{opt_arg_name}", 'not supported') { |s| not_migrate_options << not_migrate_option }
|
96
|
+
end
|
97
|
+
|
98
|
+
arg = op.cmd_parse
|
99
|
+
|
100
|
+
unless %w(mysql csv tsv).include?(options['format'])
|
101
|
+
raise ParameterConfigurationError, "#{options['format']} is unknown format. Support format is csv, tsv and mysql."
|
102
|
+
end
|
103
|
+
|
104
|
+
unless not_migrate_options.empty?
|
105
|
+
be = not_migrate_options.size == 1 ? 'is' : 'are'
|
106
|
+
$stderr.puts "`#{not_migrate_options.join(', ')}` #{be} not migrate. Please, edit config file after execute guess commands."
|
107
|
+
end
|
108
|
+
|
109
|
+
$stdout.puts "Generating #{out}..."
|
110
|
+
|
111
|
+
config = generate_seed_confing(options['format'], arg, options)
|
112
|
+
config_str = YAML.dump(config)
|
113
|
+
|
114
|
+
|
115
|
+
create_file_backup(out)
|
116
|
+
File.open(out, 'w') {|f| f << config_str }
|
117
|
+
|
118
|
+
if config['out']['type'] == 'td'
|
119
|
+
show_message_for_td_output_plugin(out)
|
120
|
+
else
|
121
|
+
show_message_for_td_data_connector(out)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
80
125
|
#
|
81
126
|
# Module private methods - don't map to import:* commands
|
82
127
|
#
|
@@ -270,7 +315,7 @@ module Command
|
|
270
315
|
port = 80 if port == 0
|
271
316
|
ssl = false
|
272
317
|
end
|
273
|
-
|
318
|
+
end
|
274
319
|
|
275
320
|
sysprops << "-Dtd.api.server.scheme=#{ssl ? 'https' : 'http'}://"
|
276
321
|
sysprops << "-Dtd.api.server.host=#{host}"
|
@@ -344,5 +389,121 @@ module Command
|
|
344
389
|
version.first
|
345
390
|
end
|
346
391
|
|
392
|
+
def generate_seed_confing(format, arg, options)
|
393
|
+
case format
|
394
|
+
when 'csv', 'tsv'
|
395
|
+
if arg =~ /^s3:/
|
396
|
+
generate_s3_config(format, arg)
|
397
|
+
else
|
398
|
+
generate_csv_config(format, arg)
|
399
|
+
end
|
400
|
+
when 'mysql'
|
401
|
+
arg = arg[1] unless arg.class == String
|
402
|
+
generate_mysql_config(arg, options)
|
403
|
+
else
|
404
|
+
# NOOP
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
def generate_s3_config(format, arg)
|
409
|
+
puts_with_indent('Using S3 input')
|
410
|
+
puts_with_indent('Using CSV parser plugin')
|
411
|
+
puts_with_indent('Using Treasure Data data connector')
|
412
|
+
|
413
|
+
match = Regexp.new("^s3://(.*):(.*)@/([^/]*)/(.*)").match(arg)
|
414
|
+
|
415
|
+
{
|
416
|
+
'in' => {
|
417
|
+
'type' => 's3',
|
418
|
+
'access_key_id' => match[1],
|
419
|
+
'secret_access_key' => match[2],
|
420
|
+
'bucket' => match[3],
|
421
|
+
'path_prefix' => normalize_path_prefix(match[4])
|
422
|
+
},
|
423
|
+
'out' => {'mode' => 'append'}
|
424
|
+
}
|
425
|
+
end
|
426
|
+
|
427
|
+
def generate_csv_config(format, arg)
|
428
|
+
puts_with_indent('Using local file input')
|
429
|
+
puts_with_indent('Using CSV parser plugin')
|
430
|
+
puts_with_indent('Using Treasure Data output')
|
431
|
+
|
432
|
+
{
|
433
|
+
'in' => {
|
434
|
+
'type' => 'file',
|
435
|
+
'path_prefix' => normalize_path_prefix(arg),
|
436
|
+
'decorders' => [{'type' => 'gzip'}],
|
437
|
+
},
|
438
|
+
'out' => td_output_config,
|
439
|
+
}
|
440
|
+
end
|
441
|
+
|
442
|
+
def td_output_config
|
443
|
+
{
|
444
|
+
'type' => 'td',
|
445
|
+
'endpoint' => Config.cl_endpoint || Config.endpoint,
|
446
|
+
'apikey' => Config.cl_apikey || Config.apikey,
|
447
|
+
'database' => '',
|
448
|
+
'table' => '',
|
449
|
+
}
|
450
|
+
end
|
451
|
+
|
452
|
+
def normalize_path_prefix(path)
|
453
|
+
path.gsub(/\*.*/, '')
|
454
|
+
end
|
455
|
+
|
456
|
+
def generate_mysql_config(arg, options)
|
457
|
+
puts_with_indent('Using MySQL input')
|
458
|
+
puts_with_indent('Using MySQL parser plugin')
|
459
|
+
puts_with_indent('Using Treasure Data output')
|
460
|
+
|
461
|
+
mysql_url_regexp = Regexp.new("[jdbc:]*mysql://(?<host>[^:/]*)[:]*(?<port>[^/]*)/(?<db_name>.*)")
|
462
|
+
|
463
|
+
config = if (match = mysql_url_regexp.match(options['db_url']))
|
464
|
+
{
|
465
|
+
'host' => match['host'],
|
466
|
+
'port' => match['port'] == '' ? 3306 : match['port'].to_i,
|
467
|
+
'database' => match['db_name'],
|
468
|
+
}
|
469
|
+
else
|
470
|
+
{
|
471
|
+
'host' => '',
|
472
|
+
'port' => 3306,
|
473
|
+
'database' => '',
|
474
|
+
}
|
475
|
+
end
|
476
|
+
|
477
|
+
{
|
478
|
+
'in' => config.merge(
|
479
|
+
'type' => 'mysql',
|
480
|
+
'user' => options['db_user'],
|
481
|
+
'password' => options['db_password'],
|
482
|
+
'table' => arg,
|
483
|
+
'select' => '*',
|
484
|
+
),
|
485
|
+
'out' => td_output_config,
|
486
|
+
}
|
487
|
+
end
|
488
|
+
|
489
|
+
def show_message_for_td_output_plugin(out)
|
490
|
+
$stdout.puts 'Done. Please use embulk to load the files.'
|
491
|
+
$stdout.puts 'Next steps:'
|
492
|
+
$stdout.puts
|
493
|
+
puts_with_indent '# install embulk'
|
494
|
+
puts_with_indent "$ embulk gem install embulk-output-td"
|
495
|
+
puts_with_indent '$ embulk guess seed.yml -o config.yml'
|
496
|
+
puts_with_indent '$ embulk preview config.yml'
|
497
|
+
puts_with_indent '$ embulk run config.yml'
|
498
|
+
end
|
499
|
+
|
500
|
+
def show_message_for_td_data_connector(out)
|
501
|
+
$stdout.puts 'Done. Please use connector:guess and connector:run to load the files.'
|
502
|
+
$stdout.puts 'Next steps:'
|
503
|
+
puts_with_indent "$ td connector:guess #{out} -o config.yml"
|
504
|
+
puts_with_indent '$ td connector:preview config.yml'
|
505
|
+
puts_with_indent '$ td connector:run config.yml'
|
506
|
+
end
|
507
|
+
|
347
508
|
end
|
348
509
|
end
|