flydata 0.6.3 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -2
  3. data/VERSION +1 -1
  4. data/bin/fdredshift +78 -0
  5. data/circle.yml +1 -1
  6. data/ext/flydata/{parser/mysql → source_mysql/parser}/.gitignore +0 -0
  7. data/ext/flydata/{parser/mysql → source_mysql/parser}/dump_parser_ext.cpp +3 -3
  8. data/ext/flydata/source_mysql/parser/extconf.rb +3 -0
  9. data/ext/flydata/{parser/mysql → source_mysql/parser}/parser.txt +0 -0
  10. data/ext/flydata/{parser/mysql → source_mysql/parser}/sql_parser.cpp +0 -0
  11. data/ext/flydata/{parser/mysql → source_mysql/parser}/sql_parser.h +0 -0
  12. data/flydata-core/lib/flydata-core/mysql/binlog_pos.rb +34 -32
  13. data/flydata-core/lib/flydata-core/mysql/compatibility_checker.rb +20 -0
  14. data/flydata-core/lib/flydata-core/table_def/mysql_table_def.rb +12 -4
  15. data/flydata-core/lib/flydata-core/table_def/redshift_table_def.rb +60 -6
  16. data/flydata-core/spec/mysql/binlog_pos_spec.rb +474 -0
  17. data/flydata-core/spec/table_def/mysql_table_def_spec.rb +57 -0
  18. data/flydata-core/spec/table_def/mysql_to_redshift_table_def_spec.rb +174 -20
  19. data/flydata-core/spec/table_def/mysqldump_test_col_comment_with_AUTO_INCREMENT_keyword.dump +43 -0
  20. data/flydata-core/spec/table_def/mysqldump_test_col_comment_with_not_null_keyword.dump +43 -0
  21. data/flydata-core/spec/table_def/mysqldump_test_col_comment_with_unique_keyword.dump +43 -0
  22. data/flydata-core/spec/table_def/mysqldump_test_col_comment_with_unsigned_keyword.dump +43 -0
  23. data/flydata-core/spec/table_def/redshift_table_def_spec.rb +41 -8
  24. data/flydata.gemspec +0 -0
  25. data/lib/flydata/cli.rb +11 -5
  26. data/lib/flydata/command/base.rb +14 -1
  27. data/lib/flydata/command/exclusive_runnable.rb +42 -12
  28. data/lib/flydata/command/helper.rb +6 -6
  29. data/lib/flydata/command/sender.rb +4 -3
  30. data/lib/flydata/command/setup.rb +30 -381
  31. data/lib/flydata/command/stop.rb +1 -0
  32. data/lib/flydata/command/sync.rb +273 -301
  33. data/lib/flydata/compatibility_check.rb +24 -117
  34. data/lib/flydata/fluent-plugins/in_mysql_binlog_flydata.rb +3 -3
  35. data/lib/flydata/fluent-plugins/mysql/alter_table_query_handler.rb +2 -2
  36. data/lib/flydata/fluent-plugins/mysql/binlog_record_handler.rb +6 -6
  37. data/lib/flydata/fluent-plugins/mysql/truncate_table_query_handler.rb +0 -1
  38. data/lib/flydata/parser.rb +14 -0
  39. data/lib/flydata/{parser_provider.rb → parser/parser_provider.rb} +6 -4
  40. data/lib/flydata/parser/source_table.rb +33 -0
  41. data/lib/flydata/source.rb +105 -0
  42. data/lib/flydata/source/component.rb +21 -0
  43. data/lib/flydata/source/errors.rb +7 -0
  44. data/lib/flydata/source/generate_source_dump.rb +72 -0
  45. data/lib/flydata/source/parse_dump_and_send.rb +52 -0
  46. data/lib/flydata/source/setup.rb +31 -0
  47. data/lib/flydata/source/source_pos.rb +45 -0
  48. data/lib/flydata/source/sync.rb +56 -0
  49. data/lib/flydata/source/sync_generate_table_ddl.rb +43 -0
  50. data/lib/flydata/source_file/setup.rb +17 -0
  51. data/lib/flydata/source_file/sync.rb +14 -0
  52. data/lib/flydata/{command → source_mysql/command}/mysql.rb +2 -1
  53. data/lib/flydata/{command → source_mysql/command}/mysql_command_base.rb +2 -4
  54. data/lib/flydata/{command → source_mysql/command}/mysqlbinlog.rb +2 -1
  55. data/lib/flydata/{command → source_mysql/command}/mysqldump.rb +2 -1
  56. data/lib/flydata/source_mysql/generate_source_dump.rb +53 -0
  57. data/lib/flydata/source_mysql/mysql_compatibility_check.rb +114 -0
  58. data/lib/flydata/source_mysql/parse_dump_and_send.rb +28 -0
  59. data/lib/flydata/{parser/mysql → source_mysql/parser}/.gitignore +0 -0
  60. data/lib/flydata/{parser/mysql → source_mysql/parser}/dump_parser.rb +32 -67
  61. data/lib/flydata/{parser/mysql → source_mysql/parser}/mysql_alter_table.treetop +0 -0
  62. data/lib/flydata/source_mysql/setup.rb +24 -0
  63. data/lib/flydata/source_mysql/source_pos.rb +21 -0
  64. data/lib/flydata/source_mysql/sync.rb +45 -0
  65. data/lib/flydata/source_mysql/sync_generate_table_ddl.rb +40 -0
  66. data/lib/flydata/{mysql → source_mysql}/table_ddl.rb +6 -17
  67. data/lib/flydata/source_zendesk/sync_generate_table_ddl.rb +30 -0
  68. data/lib/flydata/source_zendesk/zendesk_flydata_tabledefs.rb +133 -0
  69. data/lib/flydata/sync_file_manager.rb +132 -73
  70. data/lib/flydata/table_ddl.rb +18 -0
  71. data/spec/flydata/cli_spec.rb +1 -0
  72. data/spec/flydata/command/exclusive_runnable_spec.rb +19 -8
  73. data/spec/flydata/command/sender_spec.rb +1 -1
  74. data/spec/flydata/command/setup_spec.rb +4 -4
  75. data/spec/flydata/command/sync_spec.rb +97 -134
  76. data/spec/flydata/compatibility_check_spec.rb +16 -289
  77. data/spec/flydata/fluent-plugins/mysql/alter_table_query_handler_spec.rb +3 -3
  78. data/spec/flydata/fluent-plugins/mysql/dml_record_handler_spec.rb +1 -1
  79. data/spec/flydata/fluent-plugins/mysql/shared_query_handler_context.rb +4 -2
  80. data/spec/flydata/fluent-plugins/mysql/truncate_query_handler_spec.rb +1 -1
  81. data/spec/flydata/source_mysql/generate_source_dump_spec.rb +69 -0
  82. data/spec/flydata/source_mysql/mysql_compatibility_check_spec.rb +280 -0
  83. data/spec/flydata/{parser/mysql → source_mysql/parser}/alter_table_parser_spec.rb +2 -2
  84. data/spec/flydata/{parser/mysql → source_mysql/parser}/dump_parser_spec.rb +75 -70
  85. data/spec/flydata/source_mysql/sync_generate_table_ddl_spec.rb +137 -0
  86. data/spec/flydata/{mysql → source_mysql}/table_ddl_spec.rb +2 -2
  87. data/spec/flydata/source_spec.rb +140 -0
  88. data/spec/flydata/source_zendesk/sync_generate_table_ddl_spec.rb +33 -0
  89. data/spec/flydata/sync_file_manager_spec.rb +157 -77
  90. data/tmpl/redshift_mysql_data_entry.conf.tmpl +1 -1
  91. metadata +56 -23
  92. data/ext/flydata/parser/mysql/extconf.rb +0 -3
  93. data/lib/flydata/mysql/binlog_position.rb +0 -22
  94. data/spec/flydata/mysql/binlog_position_spec.rb +0 -35
@@ -9,6 +9,7 @@ module Flydata
9
9
  Slop.new do
10
10
  on 'n', 'no-daemon', 'Start FlyData agent as a regular program'
11
11
  on 'e', 'no-email', 'Skip sending init-sync-start notification email'
12
+ on 'force-run', 'Run forcefully, ignoring exclusive run info'
12
13
  end
13
14
  end
14
15
  def start(options_or_show_final_message = {show_final_message: true}) # For backward compatibility. Use only as options going forward
@@ -56,10 +57,10 @@ module Flydata
56
57
  end
57
58
 
58
59
  quiet_option = options[:quiet]
59
- # surpress messages if fluentd is started in #try_mysql_sync
60
+ # surpress messages if fluentd is started in #try_initial_sync
60
61
  options[:quiet] = true
61
- Flydata::Command::Sync.new.try_mysql_sync(
62
- binlog_ready_callback: start_fluentd,
62
+ Flydata::Command::Sync.new.try_initial_sync(
63
+ source_pos_ready_callback: start_fluentd,
63
64
  no_email: opts.no_email?)
64
65
  options[:quiet] = quiet_option
65
66
  start_fluentd.call unless fluentd_started
@@ -3,27 +3,10 @@ require 'flydata/command/conf'
3
3
  require 'flydata/command/login'
4
4
  require 'flydata/command/sender'
5
5
  require 'flydata/command/helper'
6
- require 'flydata/helpers'
7
- require 'flydata/sync_file_manager'
8
6
 
9
7
  module Flydata
10
8
  module Command
11
9
  class Setup < Base
12
- include Helpers
13
- LOG_PATH_EXAMPLES=
14
- %w(/var/log/httpd/access_log /var/log/apache2/access.log
15
- /var/log/httpd-access.log /var/log/apache2/access_log
16
- /var/log/messages /var/log/maillog /var/log/mysql/error.log
17
- /home/*/deploy/shared/log/*.log)
18
- OTHER = '-- None of above --'
19
- ENTER_TABLE_NAME = '-- Create a table on Redshift from your logs --'
20
-
21
- # readline settings for asking log path
22
- Readline.completion_append_character = nil
23
- Readline.completion_proc = lambda do |prefix|
24
- files = Dir["#{prefix}*"]
25
- files.map { |f| File.expand_path(f) }.map { |f| File.directory?(f) ? f + "/" : f }
26
- end
27
10
 
28
11
  ALL_DONE_MESSAGE_TEMPLATE = <<-EOM
29
12
  Congratulations! FlyData has started uploading your data.
@@ -63,7 +46,7 @@ What's next?
63
46
  $ flydata sync:generate_table_ddl > create_table.sql
64
47
 
65
48
  2. Create tables on Redshift by running the 'create_table.sql' script on your Redshift cluster.
66
- The script is auto-generated from your MySQL tables. You can also edit the script to add extra Redshift parameters such as distkey and sortkey. Once you run the script and create tables in Redshift, you are ready for the next step.
49
+ The script is auto-generated from your MySQL tables. You can also edit the script to add extra Redshift parameters such as distkey and sortkey. Once you run the script and create tables in Redshift, you are ready for the next step.
67
50
 
68
51
  3. Start Sync
69
52
  Run the following command on your server. The command will start synchronizing data between MySQL and Redshift!
@@ -85,386 +68,52 @@ What's next?
85
68
  EOM
86
69
 
87
70
  def initial_run
88
- data_port = flydata.data_port.get
89
- dashboard_url = "#{flydata.flydata_api_host}/dashboard"
90
- redshift_console_url = "#{flydata.flydata_api_host}/redshift_clusters/query/new"
91
- last_message = ALL_DONE_MESSAGE_TEMPLATE % [redshift_console_url, dashboard_url]
92
-
93
71
  # Surprisingly, Helper's start method kills the process after starting
94
72
  # the helper thanks to ServerEngine. So the command needs to run in a
95
73
  # separate process.
96
74
  fork { Flydata::Command::Helper.new.restart(quiet: true) }
97
- run(quiet: true) do
98
- Flydata::Command::Conf.new.copy_templates
99
- puts
100
- if has_registered_redshift_entries?
101
- Flydata::Command::Sender.new.stop(quiet: true)
102
- true
103
- elsif has_registered_redshift_mysql_data_entries?
104
- de = retrieve_data_entries.first
105
- if File.exists?(Flydata::SyncFileManager.new(de).binlog_path)
106
- sender = Flydata::Command::Sender.new
107
- if sender.process_exist?
108
- sender.stop(quiet: true)
109
- true
75
+ Flydata::Command::Login.new.run unless flydata.credentials.authenticated?
76
+ Flydata::Command::Conf.new.copy_templates
77
+ puts
78
+ de_exists = !!retrieve_data_entries.first
79
+ ret = if de_exists && source.setup.initial_run_need_restart?
80
+ sender = Flydata::Command::Sender.new
81
+ if sender.process_exist?
82
+ sender.stop(quiet: true)
83
+ true
84
+ else
85
+ false
86
+ end
110
87
  else
111
88
  false
112
89
  end
113
- else
114
- last_message = INITIAL_SYNC_MESSAGE_TEMPLATE
115
- false
116
- end
117
- else
118
- last_message = NO_DE_CANCEL_MESSAGE_TEMPLATE % [dashboard_url]
119
- false
120
- end
90
+ Flydata::Command::Sender.new.restart(quiet: true) if ret
91
+ last_message = de_exists ? source.setup.initial_run_complete_message
92
+ : :no_de_cancel
93
+ msgs = standard_messages
94
+ if msgs.has_key?(last_message)
95
+ last_message = msgs[last_message]
121
96
  end
122
97
  puts last_message
123
98
  end
124
99
 
125
100
  def run(options = {}, &block)
126
- Flydata::Command::Login.new.run unless flydata.credentials.authenticated?
127
- ret = block_given? ? yield : _run
128
- Flydata::Command::Sender.new.restart(options) if ret
129
- end
130
-
131
- private
132
- def _run
133
- #ask redshift
134
- case ask_data_entry_type
135
- when :redshift
136
- start_redshift_mode
137
- when :s3backup
138
- start_s3_backup_mode
139
- when :restart_flydata
140
- true
141
- else
142
- false
143
- end
144
- end
145
- def ask_data_entry_type
146
- choose_one('Choose an option', nil,
147
- ["Setup Redshift and S3 Backup", "Setup S3 Backup only", "Restart FlyData", "Cancel"],
148
- [:redshift, :s3backup, :restart_flydata, :cancel])
149
- end
150
-
151
- #### flydata-sync(RedshiftMysqlDataEntry)
152
- def show_registered_redshift_mysql_data_entries
153
- show_registered_entries('RedshiftMysqlDataEntry') do |de|
154
- say(" - #{de['display_name']}: flydata-sync (mysql -> redshift)")
155
- end
156
- end
157
-
158
- def has_registered_redshift_mysql_data_entries?
159
- has_registered_entries?('RedshiftMysqlDataEntry')
160
- end
161
-
162
- #### redshift backup mode
163
- def start_redshift_mode
164
- newline
165
- show_registered_redshift_entries
166
- newline
167
- ask_adding_new_redshift
168
- end
169
-
170
- def show_registered_redshift_entries
171
- show_registered_entries('RedshiftFileDataEntry') do |de|
172
- sn = de['redshift_schema_name']
173
- tn = de['redshift_table_name']
174
- table_name = sn.to_s.empty? ? tn : "#{sn}.#{tn}"
175
- say(" - #{de['display_name']}: #{de['log_path']} -> #{table_name} (#{table_name}_dev)")
101
+ unless data_entry
102
+ raise "No data entry exists. Please create one from FlyData Console (#{dashboard_url})"
176
103
  end
104
+ # 'flydata setup' command is deprecated
105
+ raise "You can set up FlyData on FlyData Console (#{dashboard_url})"
177
106
  end
178
107
 
179
- def has_registered_redshift_entries?
180
- has_registered_entries?('RedshiftFileDataEntry')
181
- end
182
-
183
- def ask_adding_new_redshift
184
- puts "Start registration of a new entry:"
185
- newline
186
- # select table on redshift
187
- puts "[Select a Table from your Redshift cluster]"
188
- table_name = ask_redshift_table_name
189
- return unless table_name
190
- newline
191
- # enter the log path
192
- puts "[Enter a Local Log Path]"
193
- puts "Enter the absolute path of your local log that you want to upload to the '#{table_name} (#{table_name}_dev)' table on Redshift."
194
- log_path = ask_log_path("Log path (tab:completion, return:cancel) >> ")
195
- return unless log_path and not log_path.empty?
196
- newline
197
- # select the log type
198
- puts "[Select a Log Format]"
199
- log_file_type = choose_log_file_type(log_path)
200
- # confirm and save
201
- newline
202
- puts "[Confirm]"
203
- separator
204
- puts " table(redshift) -> #{table_name} (#{table_name}_dev)"
205
- puts " local log path -> #{log_path}"
206
- puts " log file format -> #{log_file_type}"
207
- separator
208
- return unless ask_yes_no("Are you sure you want to register this new entry?", true)
209
- create_redshift_log_entry(log_path, log_file_type, table_name)
210
- end
211
-
212
- def choose_log_file_type(target_path)
213
- choose_one("Please select the log file format of (#{target_path})", nil,
214
- %w(csv tsv json apache_access_log))
215
- end
216
-
217
- def ask_redshift_table_name
218
- ret = flydata.data_port.redshift_table_list
219
-
220
- all_tables = ret['table_list'].collect {|tn| tn.gsub(/_dev$/, '')}.uniq
221
- prod_tables = ret['table_list'].select {|tn| not tn.end_with?('_dev')}
222
- dev_tables = ret['table_list'].select {|tn| tn.end_with?('_dev')}
223
-
224
- if all_tables.size < 1
225
- return ask_enter_table_name
226
- end
227
-
228
- if development?
229
- menu_list = all_tables.collect do |tn|
230
- menu = ["#{tn}", "( #{tn}_dev )"]
231
- if dev_tables.index("#{tn}_dev").nil?
232
- menu << "!WARN The '#{tn}_dev' table does not exist on your Redshift cluster"
233
- else
234
- menu << "OK"
235
- end
236
- menu
237
- end
238
- menu_str_list = format_menu_list(menu_list)
239
- message = " !DEVELOPMENT MODE! FlyData will only upload to tables with '_dev' suffix in the name."
240
- else
241
- menu_list = all_tables.collect do |tn|
242
- menu = ["#{tn}", "( #{tn}_dev )"]
243
- if prod_tables.index("#{tn}").nil?
244
- menu << "!WARN '#{tn}' table does not exist"
245
- else
246
- menu << "OK"
247
- end
248
- menu
249
- end
250
- menu_str_list = format_menu_list(menu_list)
251
- message = " !PRODUCTION MODE!"
252
- end
253
- menu_str_list << ENTER_TABLE_NAME
254
- menu_str = choose_one("Please select the table on Redshift that you want to use to store your local data. \n#{message}", nil, menu_str_list)
255
- if menu_str == ENTER_TABLE_NAME
256
- ask_enter_table_name
257
- elsif menu_str
258
- menu_str.split.first
259
- else
260
- nil
261
- end
262
- end
263
-
264
- def create_redshift_log_entry(log_path, log_file_type, table_name)
265
- if table_name.include?(".")
266
- schema_name, table_name = table_name.split(".")
267
- end
268
- data_port = flydata.data_port.get
269
- flydata.data_entry.create(
270
- data_port_id: data_port['id'],
271
- log_path: log_path,
272
- log_file_type: log_file_type,
273
- redshift_table_name: table_name,
274
- redshift_schema_name: schema_name,
275
- )
276
- say("Process added successfuly!")
277
- return true
278
- end
279
-
280
- #### s3 backup mode
281
- def start_s3_backup_mode
282
- # choose entries
283
- unless (shown = show_registered_entries)
284
- list = build_recommended_entries
285
- print_recommended(list)
286
- register_all(list) if ask_yes_no("Register all of these common entries?")
287
- end
288
- unless (shown ||= show_registered_entries) and not more_entry?
289
- begin
290
- show_registered_entries unless shown
291
- shown = false
292
- choose_log_selection(list)
293
- newline
294
- end while more_entry?
295
- end
296
- return true
297
- end
298
-
299
- def show_registered_entries(type='FileDataEntry', &block)
300
- data_entries = retrieve_data_entries
301
- @last_fetched_entries = data_entries
302
- data_entries = data_entries.select {|de| de['type'] == type}
303
- if data_entries and data_entries.size > 0
304
- puts('Registered entries on FlyData: ')
305
- separator
306
- data_entries.each { |data_entry|
307
- if block_given?
308
- yield data_entry
309
- else
310
- say(" - #{data_entry['display_name']}\t#{data_entry['log_path']}")
311
- end
312
- }
313
- separator
314
- true
315
- else
316
- false
317
- end
318
- end
319
-
320
- def has_registered_entries?(type='FlyDataEntry')
321
- data_entries = retrieve_data_entries
322
- data_entries = data_entries.select {|de| de['type'] == type}
323
- data_entries && data_entries.size > 0
324
- end
325
-
326
- def choose_log_path_from_examples
327
- candidates = (`ls #{LOG_PATH_EXAMPLES.join(' ')} 2>/dev/null`).split(/\s+/)
328
- candidates = candidates.find_all{|path| File.readable?(path)}
329
- if @last_fetched_entries
330
- candidates = candidates - @last_fetched_entries.map{|v| v['log_path']}
331
- end
332
- return OTHER unless candidates.size > 0
333
- candidates << OTHER
334
- choice = nil
335
- say('Please select your log path for sending FlyData')
336
- newline
337
- choose do |menu|
338
- menu.index = :letter
339
- menu.index_suffix = ") "
340
- menu.prompt = "Your log path: "
341
- menu.choices(*candidates) {|item| choice = item}
342
- end
343
- newline
344
- choice
345
- end
346
-
347
- def ask_and_create_data_entry
348
- path = ask_log_path
349
- create_log_entry(path)
350
- end
351
-
352
- def build_recommended_entries
353
- path_options=[]
354
- Dir['/var/log*/**/*log'].each{|f| if (FileTest.file?(f) and
355
- FileTest.readable?(f) and
356
- ( f =~ /apache2|httpd|syslog|mail|auth/)) and
357
- !(@last_fetched_entries and @last_fetched_entries.any?{|e| e['log_path'] == f})
358
- then path_options << f end}
359
- path_options
360
- end
361
-
362
- def print_recommended(list, options=false)
363
- newline
364
- puts " Recommended list:"
365
- list.each_with_index { |value, index|
366
- puts " #{index+1}) #{value} "
367
- }
368
- if options
369
- puts(" #{list.length+1}) Enter your own path")
370
- end
371
- newline
372
- end
108
+ private
373
109
 
374
- def register_all(list)
375
- list.each{ |item|
376
- create_log_entry(item)
110
+ def standard_messages
111
+ {
112
+ all_done: ALL_DONE_MESSAGE_TEMPLATE %
113
+ [redshift_console_url, dashboard_url],
114
+ initial_sync: INITIAL_SYNC_MESSAGE_TEMPLATE,
115
+ no_de_cancel: NO_DE_CANCEL_MESSAGE_TEMPLATE % [dashboard_url],
377
116
  }
378
- list.reject!{|x| x}
379
- end
380
-
381
- def choose_log_selection(list)
382
- path = nil
383
- list = build_recommended_entries unless list
384
- path_options = list
385
-
386
- loop do
387
- if path_options.empty?
388
- ask_and_create_data_entry
389
- return
390
- end
391
- print_recommended(path_options, true)
392
- choice = Readline.readline("Here are some common logs, enter the number next to the logs to add the log. Input nothing to cancel.")
393
- return if choice.empty?
394
- if choice.to_i==path_options.length+1
395
- ask_and_create_data_entry
396
- return
397
- elsif (path_options[choice.to_i-1] != nil )
398
- path = path_options[choice.to_i-1]
399
- path_options.delete_at(choice.to_i-1)
400
- break
401
- else
402
- puts("Not a valid entry, please try again");
403
- end
404
- end
405
- create_log_entry(path)
406
- end
407
-
408
- def ask_log_deletion(path)
409
- unless File.writable?(path)
410
- say("Skip log deletion setting...")
411
- say(" This path is readonly for current user.")
412
- say(" Change user or permission, if you want to set log deletion option.")
413
- newline
414
- return
415
- end
416
- say("** Log deletion setting **")
417
- say("Flydata has a log deletion feature that flydata will delete old log archives uploaded by flydata automatically.")
418
- say("Flydata will delete logs whose last modified timestamp is 7 days ago.")
419
- say("For more details - http://docs.hapyrus.com/faq/how-log-deletion-works/")
420
- ask_yes_no("Set auto log deletion mode?")
421
- end
422
-
423
- def create_log_entry(path, log_deletion=false)
424
- data_port = flydata.data_port.get
425
- flydata.data_entry.create(data_port_id: data_port['id'], log_path: path, log_deletion: log_deletion)
426
- say("Process added successfuly!") if flydata.response.code == 200
427
- end
428
-
429
- def more_entry?
430
- ask_yes_no("Do you want to add another log path?")
431
- end
432
-
433
- def ask_enter_table_name
434
- input = nil
435
- loop do
436
- say("Enter a table name for Redshift to store your logs. This table will be created automatically.")
437
- say("Input format: [table-name] or [schema-name].[table-name]")
438
- say(">> ")
439
- input = gets.strip
440
- if input =~ /^([a-zA-Z0-9_][a-zA-Z0-9_$]*\.)?[a-zA-Z0-9_][a-zA-Z0-9_$]*$/
441
- break
442
- else
443
- puts "!Please enter the valid table name."
444
- end
445
- end
446
- if development?
447
- input = input.gsub(/_dev$/, '')
448
- end
449
- input
450
- end
451
-
452
- def ask_log_path(message = nil)
453
- path = nil
454
- message = "Enter the absolute path of your log (return to cancel): " unless message
455
- loop do
456
- path = Readline.readline(message)
457
- return if path.empty?
458
- if not (FileTest.file?(path) and FileTest.readable?(path))
459
- say(" ! #{path} is not a readable file!")
460
- elsif @last_fetched_entries and @last_fetched_entries.any?{|e| e['log_path'] == path}
461
- say(" ! #{path} has been registered already.")
462
- else
463
- break
464
- end
465
- newline
466
- end
467
- path
468
117
  end
469
118
  end
470
119
  end