gooddata 1.3.6-java → 2.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +54 -36
  4. data/CHANGELOG.md +37 -1
  5. data/CONTRIBUTING.md +4 -0
  6. data/Gemfile +9 -0
  7. data/Jenkinsfile-chart +14 -0
  8. data/RELEASING.md +5 -8
  9. data/Rakefile +1 -1
  10. data/SDK_VERSION +1 -1
  11. data/bin/run_brick.rb +1 -1
  12. data/bin/test_projects_cleanup.rb +20 -8
  13. data/ci.rake +2 -2
  14. data/k8s/charts/lcm-bricks/Chart.yaml +4 -0
  15. data/k8s/charts/lcm-bricks/templates/prometheus/alertingRules.yaml +96 -0
  16. data/lcm.rake +1 -1
  17. data/lib/gooddata/bricks/middleware/context_logger_decorator.rb +31 -0
  18. data/lib/gooddata/bricks/middleware/context_manager.rb +68 -0
  19. data/lib/gooddata/bricks/middleware/gooddata_middleware.rb +15 -9
  20. data/lib/gooddata/bricks/middleware/logger_middleware.rb +20 -0
  21. data/lib/gooddata/bricks/middleware/mask_logger_decorator.rb +35 -5
  22. data/lib/gooddata/client.rb +2 -3
  23. data/lib/gooddata/core/gd_logger.rb +92 -0
  24. data/lib/gooddata/core/logging.rb +24 -7
  25. data/lib/gooddata/core/nil_logger.rb +1 -2
  26. data/lib/gooddata/core/splunk_logger.rb +23 -0
  27. data/lib/gooddata/lcm/actions/synchronize_users.rb +2 -1
  28. data/lib/gooddata/lcm/helpers/check_helper.rb +3 -15
  29. data/lib/gooddata/lcm/helpers/safe_failure_helper.rb +19 -0
  30. data/lib/gooddata/lcm/lcm2.rb +21 -10
  31. data/lib/gooddata/mixins/property_accessor.rb +30 -0
  32. data/lib/gooddata/models/execution.rb +5 -0
  33. data/lib/gooddata/models/project.rb +6 -4
  34. data/lib/gooddata/rest/client.rb +17 -6
  35. data/lib/gooddata/rest/connection.rb +20 -6
  36. data/lib/gooddata/rest/rest_aggregator.rb +46 -0
  37. metadata +12 -2
@@ -0,0 +1,68 @@
1
+ # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
2
+ # This source code is licensed under the BSD-style license found in the
3
+ # LICENSE file in the root directory of this source tree.
4
+
5
+ require_relative '../../mixins/property_accessor'
6
+ require_relative '../../lcm/helpers/helpers'
7
+
8
+ module GoodData
9
+ module ContextManager
10
+ extend GoodData::Mixin::PropertyAccessor
11
+
12
+ property_accessor :@context, :action
13
+ property_accessor :@context, :brick
14
+ property_accessor :@context, :execution_id
15
+ property_accessor :@context, :status
16
+
17
+ def initialize_context
18
+ @action_start = Time.now
19
+
20
+ # :log_v is used to differentiate new versions of logs in splunk
21
+ @context = {
22
+ :api_version => GoodData.version,
23
+ :log_v => 0,
24
+ :component => 'lcm.ruby',
25
+ :status => :not_in_action
26
+ }
27
+ end
28
+
29
+ # Return current brick context extended with time specific information
30
+ #
31
+ # @param [Time] now, allows to specify exact time, when outer call was performed
32
+ # @return [Hash] Brick context
33
+ def context(now = Time.now)
34
+ time_specific_context = action ? { :time => time_from_action_start(now) } : {}
35
+ @context.merge(time_specific_context)
36
+ end
37
+
38
+ def time_from_action_start(now = Time.now)
39
+ fail_if_development 'No action is being profiled' unless action
40
+ (now - @action_start) * 1000
41
+ end
42
+
43
+ # Starts lcm action
44
+ #
45
+ # @param [String] action, name of the action
46
+ # @param [Logger] logger, logger that should log current context info
47
+ # @param [Time] now, allows to specify exact time, when outer call was performed
48
+ def start_action(next_action, logger = nil, now = Time.now)
49
+ fail_if_development 'An action is already being profiled' if action
50
+
51
+ self.action = next_action
52
+ @action_start = now
53
+ logger.info '' if logger
54
+ self.status = :action_in_progress
55
+ end
56
+
57
+ # Ends currently opened lcm action
58
+ #
59
+ # @param [Logger] logger, logger that should log current context info
60
+ def end_action(logger = nil)
61
+ fail_if_development 'No matching action to start found' unless action
62
+
63
+ logger.info '' if logger
64
+ self.status = :not_in_action
65
+ self.action = nil
66
+ end
67
+ end
68
+ end
@@ -26,6 +26,9 @@ module GoodData
26
26
  DEFAULT_HOSTNAME = 'secure.gooddata.com'
27
27
 
28
28
  def call(params)
29
+ # Generate brick execution id
30
+ execution_id = GoodData.gd_logger.execution_id
31
+
29
32
  # Convert possible jruby hash to plain hash
30
33
  params = params.to_hash
31
34
 
@@ -48,7 +51,8 @@ module GoodData
48
51
  params['GDC_VERIFY_SSL'].to_b,
49
52
  params['GDC_USERNAME'],
50
53
  params['GDC_PASSWORD'],
51
- params['GDC_SST']
54
+ params['GDC_SST'],
55
+ execution_id
52
56
  )
53
57
 
54
58
  opts = params['development_client']
@@ -66,7 +70,8 @@ module GoodData
66
70
  opts['verify_ssl'].to_b,
67
71
  opts['username'] || opts['login'] || opts['email'],
68
72
  opts['password'],
69
- opts['sst']
73
+ opts['sst'],
74
+ execution_id
70
75
  )
71
76
  else
72
77
  development_client = client
@@ -77,6 +82,7 @@ module GoodData
77
82
  'development_client' => development_client
78
83
  }
79
84
 
85
+ # collect parent project if deployed as process
80
86
  if params['GDC_PROJECT_ID']
81
87
  new_params['gdc_project'] = GoodData.project = client.projects(params['GDC_PROJECT_ID'])
82
88
  end
@@ -86,29 +92,29 @@ module GoodData
86
92
  # Try to disconnect client
87
93
  begin
88
94
  client.disconnect
89
- rescue
90
- GoodData.logger.warn('Tried to disconnect client. Was unsuccessful. Proceeding anyway.')
95
+ rescue StandardError => e
96
+ GoodData.logger.warn("Tried to disconnect client. Was unsuccessful. Proceeding anyway. Error: #{e}")
91
97
  end
92
98
 
93
99
  # Try to disconnect development_client
94
100
  begin
95
101
  development_client.disconnect if development_client != client
96
- rescue
97
- GoodData.logger.warn('Tried to disconnect development_client. Was unsuccessful. Proceeding anyway.')
102
+ rescue StandardError => e
103
+ GoodData.logger.warn("Tried to disconnect development_client. Was unsuccessful. Proceeding anyway. Error: #{e}")
98
104
  end
99
105
 
100
106
  returning_value
101
107
  end
102
108
 
103
109
  class << self
104
- def connect(server, verify_ssl, username, password, sst_token) # rubocop:disable Metrics/ParameterLists
110
+ def connect(server, verify_ssl, username, password, sst_token, execution_id) # rubocop:disable Metrics/ParameterLists
105
111
  if username.nil? || password.nil?
106
112
  GoodData.logger.info("Connecting with SST to server #{server}")
107
113
  raise 'SST (SuperSecureToken) not present in params' if sst_token.nil?
108
- conn = GoodData.connect(sst_token: sst_token, server: server, verify_ssl: verify_ssl)
114
+ conn = GoodData.connect(sst_token: sst_token, server: server, verify_ssl: verify_ssl, execution_id: execution_id)
109
115
  else
110
116
  GoodData.logger.info("Connecting as #{username} to server #{server}")
111
- conn = GoodData.connect(username, password, server: server, verify_ssl: verify_ssl)
117
+ conn = GoodData.connect(username, password, server: server, verify_ssl: verify_ssl, execution_id: execution_id)
112
118
  end
113
119
  conn.stats_on
114
120
 
@@ -5,6 +5,7 @@
5
5
  # LICENSE file in the root directory of this source tree.
6
6
 
7
7
  require 'logger'
8
+ require 'gooddata/core/splunk_logger'
8
9
 
9
10
  require 'gooddata/extensions/true'
10
11
  require 'gooddata/extensions/false'
@@ -20,6 +21,7 @@ using NilExtensions
20
21
 
21
22
  require_relative 'base_middleware'
22
23
  require_relative 'mask_logger_decorator'
24
+ require_relative 'context_logger_decorator'
23
25
 
24
26
  module GoodData
25
27
  module Bricks
@@ -45,6 +47,24 @@ module GoodData
45
47
  params['GDC_LOGGER'] = logger
46
48
  GoodData.logging_http_on if params['HTTP_LOGGING'] && params['HTTP_LOGGING'].to_b
47
49
 
50
+ # Initialize splunk logger
51
+ if params['SPLUNK_LOGGING'] && params['SPLUNK_LOGGING'].to_b
52
+ GoodData.logger.info "Statistics collecting is turned ON. All the data is anonymous."
53
+ splunk_logger = SplunkLogger.new params['SPLUNK_LOG_PATH'] || GoodData::DEFAULT_SPLUNKLOG_OUTPUT
54
+ splunk_logger.level = params['SPLUNK_LOG_LEVEL'] || GoodData::DEFAULT_SPLUNKLOG_LEVEL
55
+ splunk_logger = splunk_logger.extend(ContextLoggerDecorator)
56
+ splunk_logger.context_source = GoodData.gd_logger
57
+ values_to_mask = params['values_to_mask'] || []
58
+ values_to_mask.concat MaskLoggerDecorator.extract_values params
59
+ splunk_logger = MaskLoggerDecorator.new(splunk_logger, values_to_mask) if values_to_mask.any?
60
+ else
61
+ splunk_logger = NilLogger.new
62
+ end
63
+ GoodData.splunk_logging_on splunk_logger
64
+
65
+ # Initialize context: Execution ID
66
+ GoodData.gd_logger.execution_id = params['GDC_EXECUTION_ID'] || SecureRandom.urlsafe_base64(16)
67
+
48
68
  returning(@app.call(params)) do |_result|
49
69
  logger.info('Pipeline ending')
50
70
  end
@@ -14,6 +14,24 @@ module GoodData
14
14
  @values_to_mask = values_to_mask
15
15
  end
16
16
 
17
+ class << self
18
+ # Extract values to mask from structured data
19
+ # @param values [String] or [Hash] or [Array] structured data to be extracted
20
+ # @return [[String]] array of all String in values
21
+ def extract_values(values)
22
+ if values.is_a?(String)
23
+ [values]
24
+ elsif values.is_a?(Hash) || values.is_a?(Array)
25
+ (values.is_a?(Hash) ? values.values : values).reduce([]) do |strings, item|
26
+ strings.concat extract_values(item)
27
+ strings
28
+ end
29
+ else
30
+ []
31
+ end
32
+ end
33
+ end
34
+
17
35
  # log methods to be decorated
18
36
  %i[debug error fatal info unknown warn].each do |level|
19
37
  define_method level do |message|
@@ -21,14 +39,26 @@ module GoodData
21
39
  end
22
40
  end
23
41
 
24
- private
42
+ # Decorator pretends being inner logger itselfs.
43
+ # @return inner logger class
44
+ def class
45
+ @logger.class
46
+ end
47
+
48
+ def add(severity, message = nil, progname = nil)
49
+ mask message
50
+ mask progname
51
+ @logger.add(severity, message, progname)
52
+ end
25
53
 
26
- # Masks given message.
27
- # @param message [String] message to mask
28
- # @return masked_message [String] masked message
54
+ # Masks given message
55
+ # @param message [String] or [Hash] or [Array] message to mask
56
+ # @return masked_message [String] or [Hash] or [Array] masked message
29
57
  def mask(message)
30
58
  unless message.nil?
31
- @values_to_mask.reduce(message) do |masked_message, value_to_mask|
59
+ string = message.to_s
60
+
61
+ @values_to_mask.reduce(string) do |masked_message, value_to_mask|
32
62
  masked_message.gsub(value_to_mask, "******")
33
63
  end
34
64
  end
@@ -42,8 +42,6 @@ require_relative 'core/core'
42
42
 
43
43
  module GoodData
44
44
  class << self
45
- RELEASE_INFO_PATH = '/gdc/releaseInfo'
46
-
47
45
  # Initializes required dynamically loaded classes
48
46
  def init_module
49
47
  # Metadata packages, such as report.rb, require this to be loaded first
@@ -57,8 +55,9 @@ module GoodData
57
55
  end
58
56
 
59
57
  # Returns information about the GoodData API as a Hash (e.g. version, release time etc.)
58
+ # @deprecated The release info endpoint has been deprecated without a replacement.
60
59
  def release_info
61
- @release_info ||= @connection.get(RELEASE_INFO_PATH)['release']
60
+ fail 'The release info endpoint has been deprecated without a replacement.'
62
61
  end
63
62
  end
64
63
  end
@@ -0,0 +1,92 @@
1
+ # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
2
+ # This source code is licensed under the BSD-style license found in the
3
+ # LICENSE file in the root directory of this source tree.
4
+
5
+ require 'logger'
6
+ require 'gooddata/bricks/middleware/context_manager'
7
+ require 'gooddata/rest/rest_aggregator'
8
+
9
+ module GoodData
10
+ # This class delegates messages to multiple loggers
11
+ # By usage of [ContextManager] and [Rest::Aggregator] it stores information about context of execution
12
+ class GdLogger
13
+ severity_map = {
14
+ debug: Logger::DEBUG,
15
+ info: Logger::INFO,
16
+ warn: Logger::WARN,
17
+ error: Logger::ERROR,
18
+ fatal: Logger::FATAL,
19
+ unknown: Logger::UNKNOWN
20
+ }
21
+
22
+ attr_accessor :loggers
23
+
24
+ include ContextManager
25
+ include GoodData::Rest::Aggregator
26
+
27
+ def initialize
28
+ @loggers = {}
29
+ initialize_context
30
+ initialize_store
31
+ end
32
+
33
+ def logging_on(logger_id, logger)
34
+ loggers[logger_id] = logger
35
+ end
36
+
37
+ def logging_off(logger_id)
38
+ loggers[logger_id] = NilLogger.new
39
+ end
40
+
41
+ def logging_on?(logger_id)
42
+ loggers.key?(logger_id) && !loggers[logger_id].is_a?(NilLogger)
43
+ end
44
+
45
+ # Implementation of common logger methods
46
+ %i[debug error fatal info unknown warn].each do |severity|
47
+ # GdLogger delegates all records to all loggers
48
+ define_method severity do |progname = nil, &block|
49
+ add(severity_map[severity], nil, progname, &block)
50
+ end
51
+
52
+ # Returns [True] if any logger satisfies severity level
53
+ define_method severity.to_s + "?" do
54
+ test_severity severity
55
+ end
56
+ end
57
+
58
+ def test_severity(severity)
59
+ (loggers.values.map do |logger|
60
+ logger.send severity.to_s + "?"
61
+ end).any?
62
+ end
63
+
64
+ # Pass message to multiple loggers. By parameters some loggers might be filtered out and the message might be modified.
65
+ #
66
+ # @param severity, severity of record
67
+ # @param message, message to be logged
68
+ # @param progname, progname to be logged
69
+ def add(severity, message, progname, &block)
70
+ loggers.each do |_, logger|
71
+ logger.add(severity, message, progname, &block)
72
+ end
73
+ end
74
+
75
+ # Set logger level for specified logger
76
+ #
77
+ # @param level, severity level
78
+ # @param [Symbol] logger_name, logger which severity level should be changed
79
+ def level(level, logger_name)
80
+ loggers[logger_name].level = level
81
+ end
82
+
83
+ # Set logger level for all loggers
84
+ #
85
+ # @param level, severity level
86
+ def level=(level)
87
+ loggers.values.each do |logger|
88
+ logger.level = level
89
+ end
90
+ end
91
+ end
92
+ end
@@ -7,6 +7,8 @@
7
7
  require 'rest-client'
8
8
 
9
9
  require_relative 'nil_logger'
10
+ require_relative 'splunk_logger'
11
+ require_relative 'gd_logger'
10
12
 
11
13
  module GoodData
12
14
  DEFAULT_LOG_LEVEL = Logger::INFO
@@ -17,8 +19,12 @@ module GoodData
17
19
  DEFAULT_RESTLOG_OUTPUT = STDOUT
18
20
  DEFAULT_RESTLOGGER_CLASS = Logger
19
21
 
22
+ DEFAULT_SPLUNKLOG_LEVEL = Logger::INFO
23
+ DEFAULT_SPLUNKLOG_OUTPUT = STDERR
24
+ DEFAULT_SPLUNKLOGGER_CLASS = SplunkLogger
25
+
20
26
  class << self
21
- attr_accessor :logger, :rest_logger
27
+ attr_accessor :logger, :rest_logger, :gd_logger
22
28
  attr_writer :stats
23
29
 
24
30
  # Turn logging on
@@ -80,16 +86,23 @@ module GoodData
80
86
  !@rest_logger.instance_of?(NilLogger)
81
87
  end
82
88
 
83
- def stats_on
84
- @stats = true
89
+ # Turn splunk logging on
90
+ def splunk_logging_on(logger)
91
+ gd_logger.logging_on :splunk, logger
85
92
  end
86
93
 
87
- def stats_on?
88
- @stats
94
+ # Turn splunk logging off
95
+ #
96
+ # ### Example
97
+ #
98
+ # GoodData.logging_splunk_off
99
+ #
100
+ def splunk_logging_off
101
+ gd_logger.logging_off :splunk
89
102
  end
90
103
 
91
- def stats_off
92
- @stats = false
104
+ def splunk_logging_on?
105
+ gd_logger.logging_on? :splunk
93
106
  end
94
107
 
95
108
  # Initial setup of logger
@@ -101,5 +114,9 @@ module GoodData
101
114
  DEFAULT_RESTLOG_OUTPUT,
102
115
  NilLogger
103
116
  )
117
+
118
+ # Initial setup of splunk logger
119
+ GoodData.gd_logger = GdLogger.new
120
+ GoodData.splunk_logging_off
104
121
  end
105
122
  end
@@ -1,5 +1,3 @@
1
- # encoding: UTF-8
2
- #
3
1
  # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
4
2
  # This source code is licensed under the BSD-style license found in the
5
3
  # LICENSE file in the root directory of this source tree.
@@ -19,6 +17,7 @@ module GoodData
19
17
  alias_method :info, :debug
20
18
  alias_method :warn, :debug
21
19
  alias_method :error, :debug
20
+ alias_method :add, :debug
22
21
 
23
22
  def debug?
24
23
  false
@@ -0,0 +1,23 @@
1
+ # Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
2
+ # This source code is licensed under the BSD-style license found in the
3
+ # LICENSE file in the root directory of this source tree.
4
+
5
+ require 'logger'
6
+
7
+ module GoodData
8
+ # Logger that process given message to format readable by splunk
9
+ class SplunkLogger < Logger
10
+ def hash_to_string(hash)
11
+ hash.map { |pair| " #{pair[0]}=#{pair[1]}" }.join ""
12
+ end
13
+
14
+ # If the given message or progname is an instance of Hash, it's reformatted to splunk readable format.
15
+ # In case that the message or the progname contain new line character log won't be printed out.
16
+ # Otherwise splunk worker wouldn't process it correctly.
17
+ def add(severity, message = nil, progname = nil)
18
+ message = hash_to_string(message) if message.is_a? Hash
19
+ progname = hash_to_string(progname) if progname.is_a? Hash
20
+ super(severity, message, progname) unless (progname && progname.include?("\n")) || (message && message.include?("\n"))
21
+ end
22
+ end
23
+ end