td 0.12.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|