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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc5f6252aeace112a6886530da039c0761077ac0
4
- data.tar.gz: 1dfddbf8f907474702693cd7be2a23fa940d0f14
3
+ metadata.gz: f7b9e3d57fd08024e999160fe1678031a293941e
4
+ data.tar.gz: 39454d414e3afddb9e83fcf3e3c574c98911536f
5
5
  SHA512:
6
- metadata.gz: a6212b8724a6c971d5b75437c1eb540f3d72a9afddeefc23ddcea00fcedb6bc1b5a4846f6560412027eea4ae0ab7bdcef4467beab81c9395985dcbf217c96263
7
- data.tar.gz: 65523ca21f55cdf2e852a6993020d61c5a55ea14e8a4b46b6c1280067960558be7b89b5fd3c22d2568ed63cc78e3e3445e49d7318d94330e6bebeef864f5c9e3
6
+ metadata.gz: d4846a9e5e75112f0350cbb4824e5335cb2861fabc927ddcacefe81a9a4894af08da0903920db5d1b4fcfd170b571341e5b41a77293c7a572df92ca4eec487e1
7
+ data.tar.gz: 0909c9942d35536d644db19f3ad0c3bf219d78ad59388ef8cee92af5c7f0d3271c4779503d5268dda0bd65cc2bcbaf231c3d5f02369b43dbcef0af50f77c364b
@@ -16,3 +16,8 @@ script: bundle exec rake spec
16
16
  matrix:
17
17
  allow_failures:
18
18
  - rvm: ruby-head
19
+
20
+ sudo: false
21
+
22
+ notifications:
23
+ webhooks: http://td-beda.herokuapp.com/travisci_callback
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
@@ -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 DownloadProgressIndicator
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
- super(msg)
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
- msg = "\r#{@base_msg}: #{Command.humanize_elapsed_time(@start_time, time)} elapsed"
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
- $stdout.puts "\r#{@base_msg}...done" + " " * 20
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 < DownloadProgressIndicator
387
- def initialize(msg, size, perc_step = 1, min_periodicity = nil)
388
- @size = size
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 @size.nil? || @size == 0
406
- msg = "\r#{@base_msg}: #{Command.humanize_bytesize(curr_size)}"
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 / @size).round(1)
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
- msg = "\r#{@base_msg}: #{Command.humanize_bytesize(curr_size)} / #{ratio}%"
414
- $stdout.print "\r" + " " * (msg.length + 10)
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
- $stdout.print "\r#{@base_msg}...done" + " " * 20
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
 
@@ -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 = 'td-bulkload.yml'
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
- create_bulkload_job_file_backup(out)
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 = YAML.dump(job['config'])
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 = exclude = false
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, exclude)
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 => e['table']['name'],
255
+ :Database => e['database'] ? e['database']['name'] : '',
256
+ :Table => e['table'] ? e['table']['name'] : '',
239
257
  :Priority => e['priority'],
240
- :Started => Time.at(e['start_at']),
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 = exclude = false
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, exclude)
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
- if config['config']
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, exclude)
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
@@ -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
- end
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