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.
@@ -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
- @rds = FlydataCore::Mysql::MysqlCompatibilityChecker.new(@db_opts).rds?
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?
@@ -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
+ }) {}
@@ -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