conrad 2.3.1 → 2.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b41619922ba6057aff0e0c1132b13ebd674e897f6edd141a7f4fc2ce6c8fad70
4
- data.tar.gz: 381de5251326c319d50f1c4d6d2af93a1b00ffca594f5b3e423647c105cfb75a
3
+ metadata.gz: 5bea95c17248374dd5931d1de26433e47f3e47b46ef262ef89e86e8fe516040a
4
+ data.tar.gz: c1a1388a3ec0d4fcd8002a4b185f336950c92e037db2a831e8fa3cfee47627e9
5
5
  SHA512:
6
- metadata.gz: f1d65e6a2141b159ee97077ecaa4cb9917edc060b5611736a17146e7f37daea7ce88984380d89ad6ddba30ef55ba3d1c7c81be717506bb1eafa1029e7c176f1a
7
- data.tar.gz: 1455dbf8b1ac7b2a39c78c61fd000508d2cb222fa75c13aac7105f4a296a9667ccf2bc102bd9692583da303dacd8cee2aad03b28d4f68b431f503acb01bcf5e8
6
+ metadata.gz: 3cfc946e370b048f352936240516546ba5677d1e2bea493a19c8f537c93f358ad2198f33fb6ab02e6b9d590ad2169a8410fef0ce815a21b39d0b8ef5f7e0c32f
7
+ data.tar.gz: ba69a3a6d255e10700a7f6bc57672bfa3b06d51dbe2ffdf88ed6a24ebb0f536c16bf70489d9a892031b68daecc3d2c2f477d0706a42b3242b47f4262dadf9995
data/lib/conrad.rb CHANGED
@@ -1,8 +1,25 @@
1
- Gem.find_files('conrad/processors/*.rb').each { |file| require file }
2
- Gem.find_files('conrad/formatters/*.rb').each { |file| require file }
3
- Gem.find_files('conrad/emitters/*.rb').each { |file| require file }
4
- Gem.find_files('conrad/*.rb').each { |file| require file }
1
+ require 'conrad/version'
5
2
 
6
3
  # :nodoc:
7
4
  module Conrad
5
+ autoload :Collector, 'conrad/collector'
6
+ autoload :Errors, 'conrad/errors'
7
+ autoload :EmitterQueue, 'conrad/emitter_queue'
8
+ autoload :Emitters, 'conrad/emitters'
9
+ autoload :Formatters, 'conrad/formatters'
10
+ autoload :Processors, 'conrad/processors'
11
+ autoload :ProcessorStack, 'conrad/processor_stack'
12
+ autoload :Recorder, 'conrad/recorder'
13
+
14
+ class << self
15
+ # Boolean indicating if the events collected should be emitted in the
16
+ # background. Defaults to false.
17
+ def background_emit?
18
+ EmitterQueue.instance.background
19
+ end
20
+
21
+ def background_emit=(value)
22
+ EmitterQueue.instance.background = value
23
+ end
24
+ end
8
25
  end
@@ -138,10 +138,12 @@ module Conrad
138
138
  # emitted events, the error will be allowed to bubble up. This is to
139
139
  # prevent the unexpected loss of events if a single one is malformed.
140
140
  def record_events
141
- if emit_as_batch?
142
- record_events_as_batch
143
- else
144
- record_individual_events
141
+ Array(emitter).each do |emitter|
142
+ if emit_as_batch?
143
+ record_events_as_batch(emitter, events.clone)
144
+ else
145
+ record_individual_events(emitter, events.clone)
146
+ end
145
147
  end
146
148
  ensure
147
149
  reset_state
@@ -173,16 +175,20 @@ module Conrad
173
175
  end
174
176
  end
175
177
 
176
- def record_events_as_batch
177
- emitter.call(events)
178
+ def record_events_as_batch(emitter, events)
179
+ EmitterQueue.instance.enqueue do
180
+ emitter.call(events)
181
+ end
178
182
  end
179
183
 
180
- def record_individual_events
184
+ def record_individual_events(emitter, events)
181
185
  events.each do |event|
182
- begin
183
- emitter.call(event)
184
- rescue StandardError => e
185
- write_log(:error, e)
186
+ EmitterQueue.instance.enqueue do
187
+ begin
188
+ emitter.call(event)
189
+ rescue StandardError => e
190
+ write_log(:error, e)
191
+ end
186
192
  end
187
193
  end
188
194
  end
@@ -0,0 +1,58 @@
1
+ module Conrad
2
+ # Centralized event emission queue across threads
3
+ class EmitterQueue
4
+ include Singleton
5
+
6
+ # Boolean that determines whether events will be emitted inline or in a
7
+ # background thread
8
+ attr_reader :background
9
+
10
+ # Logger object used for sending log events
11
+ attr_accessor :logger
12
+
13
+ def initialize
14
+ @thread = nil
15
+ @queue = Queue.new
16
+ @logger ||= Logger.new(STDOUT)
17
+ end
18
+
19
+ # bakground setter. Will start/stop the background thread
20
+ # @param value [Boolean] assigns whether events should be processed inline
21
+ # or in a separate thread.
22
+ def background=(value)
23
+ @background = value
24
+ value ? start_thread : @thread = nil
25
+ end
26
+
27
+ # Enqueues a block
28
+ # @yield block to execute. Will either run inline or separately depending on
29
+ # whether the queue is backgrounded
30
+ def enqueue
31
+ @queue.push -> { yield }
32
+
33
+ # if it's backgounded we can break out of here, as the background
34
+ # queue will pick it up. otherwise, we need to explicitly process it
35
+ emit! unless @background
36
+ end
37
+
38
+ private
39
+
40
+ def start_thread
41
+ @thread ||= Thread.new do
42
+ Thread.current.abort_on_exception = true
43
+ loop do
44
+ emit!
45
+ break unless @background
46
+ end
47
+ end
48
+ rescue e
49
+ logger.error(e)
50
+ @thread = nil
51
+ start_thread
52
+ end
53
+
54
+ def emit!
55
+ @queue.pop.call until @queue.empty?
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conrad
4
+ # :nodoc:
5
+ module Emitters
6
+ autoload :AmazonBase, 'conrad/emitters/amazon_base'
7
+ autoload :Kinesis, 'conrad/emitters/kinesis'
8
+ autoload :Sqs, 'conrad/emitters/sqs'
9
+ autoload :Stdout, 'conrad/emitters/stdout'
10
+ end
11
+ end
@@ -0,0 +1,59 @@
1
+ require 'active_model'
2
+
3
+ module Conrad
4
+ module Emitters
5
+ # Base class for AWS-based emitters
6
+ class AmazonBase
7
+ include ::ActiveModel::Model
8
+
9
+ # @return [String, nil] the configured region
10
+ attr_accessor :region
11
+
12
+ # @deprecated Will be removed in 3.0.0, no migration path
13
+ # @return [String, nil] the configured AWS Access key ID
14
+ attr_accessor :access_key_id
15
+
16
+ # @deprecated Will be removed in 3.0.0, no migration path
17
+ # @return [String, nil] the configured AWS secret access key
18
+ attr_accessor :secret_access_key
19
+
20
+ # @return [Aws::SQS::Client] the created client
21
+ attr_accessor :client
22
+
23
+ # @param queue_url [String] the queue to send messages to
24
+ # @param region [String] region the queue lives in
25
+ # @param access_key_id [String] AWS Acesss Key ID
26
+ # @param secret_access_key [String] AWS Secret Access Key
27
+ #
28
+ # @raise [InvalidAwsCredentials] if access_key_id or secret_access_key are
29
+ # not provided AND the running environment does not have valid AWS
30
+ # credentials
31
+ # @raise [Aws::Errors::MissingRegionError] if region is not provided and
32
+ # also not set via an allowed AWS environment variable
33
+ def initialize(args = {})
34
+ super
35
+ create_client(region: region, access_key_id: access_key_id, secret_access_key: secret_access_key)
36
+ end
37
+
38
+ private
39
+
40
+ def create_client(region:, access_key_id:, secret_access_key:)
41
+ if secret_access_key.nil? || access_key_id.nil?
42
+ validate_implicit_credentials
43
+
44
+ @client = self.class.client_class.new({ region: region }.compact)
45
+ else
46
+ @client = self.class.client_class.new({
47
+ region: region,
48
+ access_key_id: access_key_id,
49
+ secret_access_key: secret_access_key
50
+ }.compact)
51
+ end
52
+ end
53
+
54
+ def validate_implicit_credentials
55
+ raise Conrad::InvalidAwsCredentials unless Aws::CredentialProviderChain.new.resolve&.set?
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,34 @@
1
+ require 'conrad/errors'
2
+
3
+ module Conrad
4
+ # A module containing all of conrad's built in event emitters for outputting
5
+ # events
6
+ module Emitters
7
+ # Basic emitter for sending events to AWS's kinesis event stream. The emitter will
8
+ # attempt to use values configured in the running environment according to
9
+ # the AWS SDK documentation (such as from ~/.aws/credentials).
10
+ class Kinesis < AmazonBase
11
+ # @return [String] the configured kinesis stream name
12
+ attr_accessor :stream_name
13
+
14
+ # Sends an event up to Kinesis
15
+ #
16
+ # @param event [String] the event to be sent as a Kinesis message body
17
+ def call(event)
18
+ client.put_record(
19
+ stream_name: stream_name,
20
+ data: event,
21
+ # There's a 256 character limit on the partition key, and it's hashed down into a value used to
22
+ # pick the shard to put the data on
23
+ partition_key: event.first(255)
24
+ )
25
+ end
26
+
27
+ class << self
28
+ def client_class
29
+ Aws::Kinesis::Client
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -8,51 +8,9 @@ module Conrad
8
8
  # is given, the given credentials will be used. Otherwise, the emitter will
9
9
  # attempt to use values configured in the running environment according to
10
10
  # the AWS SDK documentation (such as from ~/.aws/credentials).
11
- class Sqs
12
- # Error for responding with issues around SQS credential creation
13
- class InvalidAwsCredentials < ::Conrad::Error
14
- # :nodoc:
15
- def to_s
16
- 'Must provide secret_access_key and access_key_id OR rely ' \
17
- 'on configured values in the running environment.'
18
- end
19
- end
20
-
11
+ class Sqs < AmazonBase
21
12
  # @return [String] the configured SQS queue URL
22
- attr_reader :queue_url
23
-
24
- # @return [String, nil] the configured region
25
- attr_reader :region
26
-
27
- # @deprecated Will be removed in 3.0.0, no migration path
28
- # @return [String, nil] the configured AWS Access key ID
29
- attr_reader :access_key_id
30
-
31
- # @deprecated Will be removed in 3.0.0, no migration path
32
- # @return [String, nil] the configured AWS secret access key
33
- attr_reader :secret_access_key
34
-
35
- # @return [Aws::SQS::Client] the created client
36
- attr_reader :client
37
-
38
- # @param queue_url [String] the queue to send messages to
39
- # @param region [String] region the queue lives in
40
- # @param access_key_id [String] AWS Acesss Key ID
41
- # @param secret_access_key [String] AWS Secret Access Key
42
- #
43
- # @raise [InvalidAwsCredentials] if access_key_id or secret_access_key are
44
- # not provided AND the running environment does not have valid AWS
45
- # credentials
46
- # @raise [Aws::Errors::MissingRegionError] if region is not provided and
47
- # also not set via an allowed AWS environment variable
48
- def initialize(queue_url:, region: nil, access_key_id: nil, secret_access_key: nil)
49
- @queue_url = queue_url
50
- @region = region
51
- @access_key_id = access_key_id
52
- @secret_access_key = secret_access_key
53
-
54
- create_client(region: region, access_key_id: access_key_id, secret_access_key: secret_access_key)
55
- end
13
+ attr_accessor :queue_url
56
14
 
57
15
  # Sends an event up to SQS
58
16
  #
@@ -61,25 +19,11 @@ module Conrad
61
19
  client.send_message(queue_url: queue_url, message_body: event)
62
20
  end
63
21
 
64
- private
65
-
66
- def create_client(region:, access_key_id:, secret_access_key:)
67
- if secret_access_key.nil? || access_key_id.nil?
68
- validate_implicit_credentials
69
-
70
- @client = Aws::SQS::Client.new({ region: region }.compact)
71
- else
72
- @client = Aws::SQS::Client.new({
73
- region: region,
74
- access_key_id: access_key_id,
75
- secret_access_key: secret_access_key
76
- }.compact)
22
+ class << self
23
+ def client_class
24
+ Aws::SQS::Client
77
25
  end
78
26
  end
79
-
80
- def validate_implicit_credentials
81
- raise InvalidAwsCredentials unless Aws::CredentialProviderChain.new.resolve.set?
82
- end
83
27
  end
84
28
  end
85
29
  end
data/lib/conrad/errors.rb CHANGED
@@ -9,6 +9,15 @@ module Conrad
9
9
  end
10
10
  end
11
11
 
12
+ # Error for responding with issues around kinesis credential creation
13
+ class InvalidAwsCredentials < Error
14
+ # :nodoc:
15
+ def to_s
16
+ 'Must provide secret_access_key and access_key_id OR rely ' \
17
+ 'on configured values in the running environment.'
18
+ end
19
+ end
20
+
12
21
  # Error raised when the value of an event attribute is not of one of the
13
22
  # allowed types
14
23
  class ForbiddenValue < Error
@@ -0,0 +1,6 @@
1
+ module Conrad
2
+ # :nodoc:
3
+ module Formatters
4
+ autoload :JSON, 'conrad/formatters/json'
5
+ end
6
+ end
@@ -1 +1,8 @@
1
- Dir['conrad/processors/*.rb'].each { |file| require file }
1
+ module Conrad
2
+ # :nodoc:
3
+ module Processors
4
+ autoload :AddTimestamp, 'conrad/processors/add_timestamp'
5
+ autoload :AddUUID, 'conrad/processors/add_uuid'
6
+ autoload :Envelope, 'conrad/processors/envelope'
7
+ end
8
+ end
@@ -1,5 +1,5 @@
1
1
  # :nodoc:
2
2
  module Conrad
3
3
  # :nodoc:
4
- VERSION = '2.3.1'.freeze
4
+ VERSION = '2.4.0'.freeze
5
5
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conrad
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.1
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathon Anderson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-01-28 00:00:00.000000000 Z
11
+ date: 2019-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: aws-sdk
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -89,9 +103,14 @@ extra_rdoc_files: []
89
103
  files:
90
104
  - lib/conrad.rb
91
105
  - lib/conrad/collector.rb
106
+ - lib/conrad/emitter_queue.rb
107
+ - lib/conrad/emitters.rb
108
+ - lib/conrad/emitters/amazon_base.rb
109
+ - lib/conrad/emitters/kinesis.rb
92
110
  - lib/conrad/emitters/sqs.rb
93
111
  - lib/conrad/emitters/stdout.rb
94
112
  - lib/conrad/errors.rb
113
+ - lib/conrad/formatters.rb
95
114
  - lib/conrad/formatters/json.rb
96
115
  - lib/conrad/processor_stack.rb
97
116
  - lib/conrad/processors.rb
@@ -120,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
139
  version: '0'
121
140
  requirements: []
122
141
  rubyforge_project:
123
- rubygems_version: 2.7.6
142
+ rubygems_version: 2.7.3
124
143
  signing_key:
125
144
  specification_version: 4
126
145
  summary: Tool for auditing events.