flydata 0.8.9 → 0.8.9.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitsha +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +38 -27
- data/Rakefile +4 -1
- data/VERSION +1 -1
- data/circle.yml +1 -1
- data/fd-build +26 -0
- data/flydata-core/lib/flydata-core/errors.rb +3 -0
- data/flydata-core/lib/flydata-core/mysql/compatibility_checker.rb +39 -1
- data/flydata-core/lib/flydata-core/table_def/redshift_table_def.rb +14 -4
- data/flydata-core/spec/mysql/compatibility_checker_spec.rb +140 -0
- data/flydata.gemspec +0 -0
- data/gemset.nix +580 -0
- data/lib/flydata/cli.rb +3 -0
- data/lib/flydata/error_reporting.rb +89 -0
- data/lib/flydata/fluent-plugins/in_mysql_binlog_flydata.rb +2 -0
- data/lib/flydata/fluent-plugins/in_postgresql_query_based_flydata.rb +5 -1
- data/lib/flydata/query_based_sync/client.rb +6 -1
- data/lib/flydata/source_mysql/mysql_compatibility_check.rb +9 -1
- data/replication.nix +30 -0
- data/shell.nix +47 -0
- data/spec/flydata/error_reporting_spec.rb +107 -0
- data/spec/flydata/source_mysql/mysql_compatibility_check_spec.rb +38 -0
- data/spec/flydata/source_mysql/parser/dump_parser_spec.rb +3 -1
- data/spec/flydata/source_mysql/sync_generate_table_ddl_spec.rb +12 -0
- metadata +122 -100
data/lib/flydata/cli.rb
CHANGED
@@ -3,6 +3,7 @@ require 'flydata/command_loggable'
|
|
3
3
|
require 'flydata/errors'
|
4
4
|
require 'flydata/helpers'
|
5
5
|
require 'flydata/source'
|
6
|
+
require 'flydata/error_reporting'
|
6
7
|
|
7
8
|
module Flydata
|
8
9
|
class Cli
|
@@ -18,6 +19,8 @@ module Flydata
|
|
18
19
|
"#{datetime} command.#{severity.to_s.downcase}: #{msg}\n"
|
19
20
|
end
|
20
21
|
end
|
22
|
+
|
23
|
+
Flydata::RollbarHookSetup.new(get_logger).setup
|
21
24
|
log_info("Start command.", {cmd: @args.join(' '), ver:flydata_version})
|
22
25
|
end
|
23
26
|
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'flydata' # For FLYDATA_HOME
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
module Flydata
|
5
|
+
|
6
|
+
class RollbarHookSetup
|
7
|
+
def initialize(log,
|
8
|
+
access_token=ENV['ROLLBAR_ACCESS_TOKEN'],
|
9
|
+
environment=ENV['ROLLBAR_ENV'])
|
10
|
+
@log = log
|
11
|
+
@access_token = access_token
|
12
|
+
@environment = environment
|
13
|
+
end
|
14
|
+
|
15
|
+
def should_setup?
|
16
|
+
(!already_setup?) && rollbar_installed? && rollbar_flag? && config_available?
|
17
|
+
end
|
18
|
+
|
19
|
+
def rollbar_flag?
|
20
|
+
File.exists?(File.join(FLYDATA_HOME, 'use_rollbar'))
|
21
|
+
end
|
22
|
+
|
23
|
+
def config_available?
|
24
|
+
return false unless @access_token
|
25
|
+
return false if @access_token.empty?
|
26
|
+
|
27
|
+
return false unless @environment
|
28
|
+
return false if @environment.empty?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def already_setup?
|
33
|
+
@log.instance_variable_defined?('@_rollbar_hook_enabled')
|
34
|
+
end
|
35
|
+
|
36
|
+
# Use to stub in tests
|
37
|
+
def rollbar_gem_name
|
38
|
+
'rollbar'
|
39
|
+
end
|
40
|
+
|
41
|
+
def rollbar_installed?
|
42
|
+
if Gem::Specification.respond_to?(:find_all_by_name)
|
43
|
+
Gem::Specification.find_all_by_name(rollbar_gem_name).any?
|
44
|
+
else
|
45
|
+
Gem.source_index.find_name(rollbar_gem_name).first
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def rollbar_configure
|
50
|
+
return unless rollbar_installed?
|
51
|
+
require rollbar_gem_name
|
52
|
+
|
53
|
+
Rollbar.configure do |config|
|
54
|
+
# Only actually send rollbar errors to endpoint in production and staging.
|
55
|
+
config.enabled = ['production', 'staging'].include?(@environment)
|
56
|
+
config.access_token = @access_token
|
57
|
+
config.environment = @environment || 'development'
|
58
|
+
|
59
|
+
config.custom_data_method = lambda do |message, exception, context|
|
60
|
+
{ flydata_home: FLYDATA_HOME }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def setup
|
66
|
+
return false unless should_setup?
|
67
|
+
|
68
|
+
rollbar_configure
|
69
|
+
|
70
|
+
old_error = @log.method(:error)
|
71
|
+
|
72
|
+
# Replace the existing logger's error method so that it will report to rollbar messages or exceptions
|
73
|
+
# when .error is called.
|
74
|
+
@log.define_singleton_method :error do |*args|
|
75
|
+
old_error.call(*args)
|
76
|
+
|
77
|
+
if $!.nil?
|
78
|
+
Rollbar.error(args.first) # Just the message portion
|
79
|
+
else
|
80
|
+
Rollbar.error($!) # Send the exception details
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Make this method idempotent: future calls will have no effect now.
|
85
|
+
@log.instance_variable_set('@_rollbar_hook_enabled', true)
|
86
|
+
true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -18,6 +18,7 @@ require 'flydata/source_mysql/table_meta'
|
|
18
18
|
require 'flydata/source_mysql/table_ddl'
|
19
19
|
require 'flydata-core/fluent/config_helper'
|
20
20
|
require 'flydata-core/mysql/ssl'
|
21
|
+
require 'flydata/error_reporting'
|
21
22
|
|
22
23
|
|
23
24
|
class MysqlBinlogFlydataInput < MysqlBinlogInput
|
@@ -53,6 +54,7 @@ class MysqlBinlogFlydataInput < MysqlBinlogInput
|
|
53
54
|
|
54
55
|
def configure(conf)
|
55
56
|
super
|
57
|
+
Flydata::RollbarHookSetup.new($log).setup
|
56
58
|
|
57
59
|
# SSL configuration
|
58
60
|
unless @ssl_ca_content.to_s.strip.empty?
|
@@ -7,6 +7,7 @@ require 'flydata/source_postgresql/plugin_support/context'
|
|
7
7
|
require 'flydata/source_postgresql/table_meta'
|
8
8
|
require 'flydata/source_postgresql/query_based_sync/client'
|
9
9
|
require 'flydata-core/postgresql/config'
|
10
|
+
require 'flydata/error_reporting'
|
10
11
|
|
11
12
|
module Fluent
|
12
13
|
|
@@ -20,6 +21,9 @@ class PostgresqlQueryBasedFlydataInput < Input
|
|
20
21
|
|
21
22
|
def configure(conf)
|
22
23
|
super
|
24
|
+
|
25
|
+
Flydata::RollbarHookSetup.new($log).setup
|
26
|
+
|
23
27
|
@dbconf = FlydataCore::Postgresql::Config.opts_for_pg(@data_entry['postgresql_data_entry_preference'])
|
24
28
|
$log.info "postgresql host:\"#{@host}\" port:\"#{@port}\" username:\"#{@username}\" database:\"#{@database}\" tables:\"#{@tables}\" tables_append_only:\"#{@tables_append_only}\""
|
25
29
|
|
@@ -42,7 +46,7 @@ class PostgresqlQueryBasedFlydataInput < Input
|
|
42
46
|
},
|
43
47
|
)
|
44
48
|
|
45
|
-
@client = Flydata::SourcePostgresql::QueryBasedSync::Client.new(@context)
|
49
|
+
@client = Flydata::SourcePostgresql::QueryBasedSync::Client.new(@context, true)
|
46
50
|
end
|
47
51
|
|
48
52
|
def start
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'flydata-core/logger'
|
3
|
+
require 'flydata/error_reporting'
|
3
4
|
|
4
5
|
module Flydata
|
5
6
|
module QueryBasedSync
|
@@ -13,7 +14,7 @@ module QueryBasedSync
|
|
13
14
|
# params
|
14
15
|
# fetch_interval
|
15
16
|
# resource_names
|
16
|
-
def initialize(context)
|
17
|
+
def initialize(context, with_rollbar=false)
|
17
18
|
@context = context
|
18
19
|
@resource_requester = self.class::RESOURCE_REQUESTER_CLASS.new(context)
|
19
20
|
@response_handler = self.class::RESPONSE_HANDLER_CLASS.new(context)
|
@@ -22,6 +23,10 @@ module QueryBasedSync
|
|
22
23
|
@fetch_interval = c.nil? ? DEFAULT_FETCH_INTERVAL : c[:fetch_interval]
|
23
24
|
@retry_interval = c.nil? ? DEFAULT_RETRY_INTERVAL : c[:retry_interval]
|
24
25
|
end
|
26
|
+
|
27
|
+
if with_rollbar
|
28
|
+
Flydata::RollbarHookSetup.new(get_logger).setup
|
29
|
+
end
|
25
30
|
end
|
26
31
|
|
27
32
|
attr_reader :context
|
@@ -17,7 +17,13 @@ module SourceMysql
|
|
17
17
|
@dump_dir = options[:dump_dir] || nil
|
18
18
|
@backup_dir = options[:backup_dir] || nil
|
19
19
|
@tables = de_hash['tables']
|
20
|
-
|
20
|
+
|
21
|
+
begin
|
22
|
+
@rds = FlydataCore::Mysql::MysqlCompatibilityChecker.new(@db_opts).rds?
|
23
|
+
rescue FlydataCore::MissingExecutePermissionMysqlCompatibilityError => e
|
24
|
+
@rds = false
|
25
|
+
log_warn_stderr("[WARNING] #{e.message}")
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
29
|
def print_errors
|
@@ -98,6 +104,8 @@ module SourceMysql
|
|
98
104
|
else
|
99
105
|
raise e
|
100
106
|
end
|
107
|
+
rescue FlydataCore::MissingExecutePermissionMysqlCompatibilityError => e
|
108
|
+
log_warn_stderr("[WARNING] #{e.message}")
|
101
109
|
end
|
102
110
|
|
103
111
|
def is_rds?
|
data/replication.nix
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
{ pkgs ? import <nixpkgs> {}
|
2
|
+
, fd-pkgs ? pkgs.fd-pkgs
|
3
|
+
}:
|
4
|
+
fd-pkgs.callPackage (
|
5
|
+
{ fd-base-dev-env
|
6
|
+
, stdenv
|
7
|
+
, cmake
|
8
|
+
, openssl
|
9
|
+
, gnustep
|
10
|
+
, fd-mysql
|
11
|
+
, fetchFromGitHub
|
12
|
+
, boost159
|
13
|
+
}:
|
14
|
+
|
15
|
+
stdenv.mkDerivation {
|
16
|
+
name = "mysql-replication-listener";
|
17
|
+
src = fetchFromGitHub {
|
18
|
+
owner = "flydata";
|
19
|
+
repo = "mysql-replication-listener";
|
20
|
+
rev = "80d10ad92a78833fc3d145f86152a0ba99891725";
|
21
|
+
sha256 = "11020wifib046yq9j0n1fjz4i6cq28md8p549w9phgjz13a74f5n";
|
22
|
+
};
|
23
|
+
|
24
|
+
buildInputs = [
|
25
|
+
fd-base-dev-env
|
26
|
+
cmake
|
27
|
+
openssl
|
28
|
+
boost159
|
29
|
+
];
|
30
|
+
}) {}
|
data/shell.nix
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
{ pkgs ? import <nixpkgs> {}
|
2
|
+
, fd-pkgs ? pkgs.fd-pkgs
|
3
|
+
}:
|
4
|
+
|
5
|
+
let
|
6
|
+
replication = fd-pkgs.callPackage ./replication.nix {};
|
7
|
+
in
|
8
|
+
|
9
|
+
fd-pkgs.callPackage (
|
10
|
+
{ fd-base-dev-env
|
11
|
+
, stdenv
|
12
|
+
, openssl
|
13
|
+
, gnustep
|
14
|
+
, libiconv
|
15
|
+
, libxml2
|
16
|
+
, pkgconfig
|
17
|
+
, libxslt
|
18
|
+
, fd-mysql
|
19
|
+
, postgresql
|
20
|
+
, boost159
|
21
|
+
, sqlite
|
22
|
+
}:
|
23
|
+
|
24
|
+
stdenv.mkDerivation {
|
25
|
+
name = "flydata-agent-dev";
|
26
|
+
buildInputs = [
|
27
|
+
postgresql
|
28
|
+
gnustep.libobjc
|
29
|
+
libiconv
|
30
|
+
libxml2
|
31
|
+
pkgconfig
|
32
|
+
libxslt
|
33
|
+
openssl
|
34
|
+
fd-mysql.connector-c
|
35
|
+
replication
|
36
|
+
boost159
|
37
|
+
sqlite
|
38
|
+
fd-base-dev-env
|
39
|
+
];
|
40
|
+
|
41
|
+
projectDir = builtins.toString ./.;
|
42
|
+
|
43
|
+
shellHook = ''
|
44
|
+
! (gem list | grep bundler &>/dev/null) && gem install bundler
|
45
|
+
export PATH=$projectDir/bin:$PATH
|
46
|
+
'';
|
47
|
+
}) {}
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'flydata/error_reporting'
|
2
|
+
require 'logger'
|
3
|
+
require 'rollbar'
|
4
|
+
|
5
|
+
module Flydata
|
6
|
+
describe RollbarHookSetup do
|
7
|
+
before :each do
|
8
|
+
allow(setup_obj).to receive(:rollbar_gem_name).and_return(rollbar_gem_name)
|
9
|
+
allow(File).to receive(:exists?).and_return(use_rollbar_exists)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:setup_obj) {RollbarHookSetup.new(log, token, env)}
|
13
|
+
let(:log) {Logger.new(STDOUT)}
|
14
|
+
let(:token) {'sometoken'}
|
15
|
+
let(:env) {'test'}
|
16
|
+
let(:rollbar_gem_name) {'rollbar'}
|
17
|
+
let(:use_rollbar_exists) {true}
|
18
|
+
let(:log_method) {:error}
|
19
|
+
let(:log_message) {"my message"}
|
20
|
+
let(:log_args) {[log_message]}
|
21
|
+
|
22
|
+
describe 'custom data method' do
|
23
|
+
subject {setup_obj.setup; Rollbar.notifier.configuration.custom_data_method.call('a', Exception.new, Object.new)}
|
24
|
+
|
25
|
+
it {should include(:flydata_home)}
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'log calls after #setup' do
|
29
|
+
subject {setup_obj.setup; log.send(log_method, *log_args)}
|
30
|
+
|
31
|
+
it do
|
32
|
+
expect(Rollbar.notifier).to receive(:configure)
|
33
|
+
expect(Rollbar.notifier).to receive(:log).with("error", log_message)
|
34
|
+
subject
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'during an exception' do
|
38
|
+
let (:exception) { Exception.new("oh no") }
|
39
|
+
subject do
|
40
|
+
setup_obj.setup
|
41
|
+
begin
|
42
|
+
raise exception
|
43
|
+
rescue Exception
|
44
|
+
log.send(log_method, *log_args)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it do
|
49
|
+
expect(Rollbar.notifier).to receive(:log).with("error", exception)
|
50
|
+
subject
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'no environment is specified' do
|
55
|
+
let(:env) { nil }
|
56
|
+
|
57
|
+
it do
|
58
|
+
expect(Rollbar.notifier).to_not receive(:log)
|
59
|
+
expect(Rollbar.notifier).to_not receive(:configure)
|
60
|
+
subject
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
context 'no token is specified' do
|
66
|
+
let(:token) { nil }
|
67
|
+
|
68
|
+
it do
|
69
|
+
expect(Rollbar.notifier).to_not receive(:log)
|
70
|
+
expect(Rollbar.notifier).to_not receive(:configure)
|
71
|
+
subject
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'the log method is not error' do
|
76
|
+
let(:log_method) { :warn }
|
77
|
+
|
78
|
+
it do
|
79
|
+
expect(Rollbar.notifier).to_not receive(:log)
|
80
|
+
expect(Rollbar.notifier).to receive(:configure)
|
81
|
+
subject
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
context 'the use_rollbar file does not exist' do
|
87
|
+
let(:use_rollbar_exists) { false }
|
88
|
+
|
89
|
+
it do
|
90
|
+
expect(Rollbar.notifier).to_not receive(:log)
|
91
|
+
expect(Rollbar.notifier).to_not receive(:configure)
|
92
|
+
subject
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'when the rollbar gem is not installed' do
|
97
|
+
let(:rollbar_gem_name) {'rollbar2'} # Fake name that should not be installed
|
98
|
+
|
99
|
+
it do
|
100
|
+
expect(Rollbar.notifier).to_not receive(:log)
|
101
|
+
expect(Rollbar.notifier).to_not receive(:configure)
|
102
|
+
subject
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -31,6 +31,7 @@ module SourceMysql
|
|
31
31
|
|
32
32
|
let(:client) { double('client') }
|
33
33
|
before do
|
34
|
+
allow_any_instance_of(FlydataCore::Mysql::MysqlCompatibilityChecker).to receive(:rds?).and_return(false)
|
34
35
|
allow(Mysql2::Client).to receive(:new).and_return(client)
|
35
36
|
allow(client).to receive(:query).with("call mysql.rds_show_configuration;")
|
36
37
|
allow(client).to receive(:close)
|
@@ -156,6 +157,7 @@ module SourceMysql
|
|
156
157
|
allow(subject_object).to receive(:is_rds?).and_return(true)
|
157
158
|
end
|
158
159
|
before do
|
160
|
+
allow_any_instance_of(FlydataCore::Mysql::ExecutePrivilegeChecker).to receive(:do_check)
|
159
161
|
allow(client).to receive(:query).with("SELECT @@expire_logs_days").and_return([{"@@expire_logs_days"=>0}])
|
160
162
|
end
|
161
163
|
|
@@ -220,6 +222,7 @@ module SourceMysql
|
|
220
222
|
context 'when host is rds' do
|
221
223
|
before do
|
222
224
|
de_hash['host'] = 'rdrss.xxyyzz.rds.amazonaws.com'
|
225
|
+
allow_any_instance_of(FlydataCore::Mysql::MysqlCompatibilityChecker).to receive(:rds?).and_call_original
|
223
226
|
end
|
224
227
|
|
225
228
|
context "where backup retention period is not set" do
|
@@ -241,6 +244,41 @@ module SourceMysql
|
|
241
244
|
end
|
242
245
|
end
|
243
246
|
end
|
247
|
+
|
248
|
+
describe '#run_rds_retention_check' do
|
249
|
+
subject { subject_object.run_rds_retention_check }
|
250
|
+
|
251
|
+
before do
|
252
|
+
allow_any_instance_of(FlydataCore::Mysql::ExecutePrivilegeChecker).to receive(:do_check)
|
253
|
+
allow_any_instance_of(FlydataCore::Mysql::RdsRetentionChecker).to receive(:do_check)
|
254
|
+
end
|
255
|
+
|
256
|
+
context 'when RdsRetentionChecker succeeds' do
|
257
|
+
it { expect{subject}.to_not raise_error }
|
258
|
+
end
|
259
|
+
|
260
|
+
context 'when RdsRetentionChecker fails with command denied' do
|
261
|
+
before do
|
262
|
+
allow_any_instance_of(FlydataCore::Mysql::RdsRetentionChecker).to receive(:do_check).and_raise(Mysql2::Error, "execute command denied to user 'test'@'%' for routine 'mysql.rds_show_configuration'")
|
263
|
+
end
|
264
|
+
|
265
|
+
it { expect{subject}.to_not raise_error }
|
266
|
+
|
267
|
+
it 'logs the error message' do
|
268
|
+
expect(subject_object).to receive(:log_warn_stderr)
|
269
|
+
|
270
|
+
subject
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
context 'when RdsRetentionChecker fails with unexpected error' do
|
275
|
+
before do
|
276
|
+
allow_any_instance_of(FlydataCore::Mysql::RdsRetentionChecker).to receive(:do_check).and_raise(Mysql2::Error, "error")
|
277
|
+
end
|
278
|
+
|
279
|
+
it { expect{subject}.to raise_error }
|
280
|
+
end
|
281
|
+
end
|
244
282
|
end
|
245
283
|
|
246
284
|
end
|