flydata 0.8.9 → 0.8.9.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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