logstash-integration-aws 0.1.0.pre

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.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.PRE.MERGE.md +658 -0
  3. data/CHANGELOG.md +15 -0
  4. data/CONTRIBUTORS +40 -0
  5. data/Gemfile +11 -0
  6. data/LICENSE +202 -0
  7. data/NOTICE.TXT +5 -0
  8. data/README.md +205 -0
  9. data/docs/codec-cloudfront.asciidoc +53 -0
  10. data/docs/codec-cloudtrail.asciidoc +45 -0
  11. data/docs/index.asciidoc +38 -0
  12. data/docs/input-cloudwatch.asciidoc +320 -0
  13. data/docs/input-s3.asciidoc +346 -0
  14. data/docs/input-sqs.asciidoc +287 -0
  15. data/docs/output-cloudwatch.asciidoc +321 -0
  16. data/docs/output-s3.asciidoc +442 -0
  17. data/docs/output-sns.asciidoc +166 -0
  18. data/docs/output-sqs.asciidoc +242 -0
  19. data/lib/logstash/codecs/cloudfront.rb +84 -0
  20. data/lib/logstash/codecs/cloudtrail.rb +47 -0
  21. data/lib/logstash/inputs/cloudwatch.rb +338 -0
  22. data/lib/logstash/inputs/s3.rb +466 -0
  23. data/lib/logstash/inputs/sqs.rb +196 -0
  24. data/lib/logstash/outputs/cloudwatch.rb +346 -0
  25. data/lib/logstash/outputs/s3/file_repository.rb +121 -0
  26. data/lib/logstash/outputs/s3/path_validator.rb +18 -0
  27. data/lib/logstash/outputs/s3/size_and_time_rotation_policy.rb +24 -0
  28. data/lib/logstash/outputs/s3/size_rotation_policy.rb +26 -0
  29. data/lib/logstash/outputs/s3/temporary_file.rb +71 -0
  30. data/lib/logstash/outputs/s3/temporary_file_factory.rb +129 -0
  31. data/lib/logstash/outputs/s3/time_rotation_policy.rb +26 -0
  32. data/lib/logstash/outputs/s3/uploader.rb +74 -0
  33. data/lib/logstash/outputs/s3/writable_directory_validator.rb +17 -0
  34. data/lib/logstash/outputs/s3/write_bucket_permission_validator.rb +60 -0
  35. data/lib/logstash/outputs/s3.rb +405 -0
  36. data/lib/logstash/outputs/sns.rb +133 -0
  37. data/lib/logstash/outputs/sqs.rb +167 -0
  38. data/lib/logstash/plugin_mixins/aws_config/generic.rb +54 -0
  39. data/lib/logstash/plugin_mixins/aws_config/v2.rb +93 -0
  40. data/lib/logstash/plugin_mixins/aws_config.rb +8 -0
  41. data/logstash-integration-aws.gemspec +52 -0
  42. data/spec/codecs/cloudfront_spec.rb +92 -0
  43. data/spec/codecs/cloudtrail_spec.rb +56 -0
  44. data/spec/fixtures/aws_credentials_file_sample_test.yml +2 -0
  45. data/spec/fixtures/aws_temporary_credentials_file_sample_test.yml +3 -0
  46. data/spec/fixtures/cloudfront.log +4 -0
  47. data/spec/fixtures/compressed.log.gee.zip +0 -0
  48. data/spec/fixtures/compressed.log.gz +0 -0
  49. data/spec/fixtures/compressed.log.gzip +0 -0
  50. data/spec/fixtures/invalid_utf8.gbk.log +2 -0
  51. data/spec/fixtures/json.log +2 -0
  52. data/spec/fixtures/json_with_message.log +2 -0
  53. data/spec/fixtures/multiline.log +6 -0
  54. data/spec/fixtures/multiple_compressed_streams.gz +0 -0
  55. data/spec/fixtures/uncompressed.log +2 -0
  56. data/spec/inputs/cloudwatch_spec.rb +85 -0
  57. data/spec/inputs/s3_spec.rb +610 -0
  58. data/spec/inputs/sincedb_spec.rb +17 -0
  59. data/spec/inputs/sqs_spec.rb +324 -0
  60. data/spec/integration/cloudwatch_spec.rb +25 -0
  61. data/spec/integration/dynamic_prefix_spec.rb +92 -0
  62. data/spec/integration/gzip_file_spec.rb +62 -0
  63. data/spec/integration/gzip_size_rotation_spec.rb +63 -0
  64. data/spec/integration/outputs/sqs_spec.rb +98 -0
  65. data/spec/integration/restore_from_crash_spec.rb +67 -0
  66. data/spec/integration/s3_spec.rb +66 -0
  67. data/spec/integration/size_rotation_spec.rb +59 -0
  68. data/spec/integration/sqs_spec.rb +110 -0
  69. data/spec/integration/stress_test_spec.rb +60 -0
  70. data/spec/integration/time_based_rotation_with_constant_write_spec.rb +60 -0
  71. data/spec/integration/time_based_rotation_with_stale_write_spec.rb +64 -0
  72. data/spec/integration/upload_current_file_on_shutdown_spec.rb +51 -0
  73. data/spec/outputs/cloudwatch_spec.rb +38 -0
  74. data/spec/outputs/s3/file_repository_spec.rb +143 -0
  75. data/spec/outputs/s3/size_and_time_rotation_policy_spec.rb +77 -0
  76. data/spec/outputs/s3/size_rotation_policy_spec.rb +41 -0
  77. data/spec/outputs/s3/temporary_file_factory_spec.rb +89 -0
  78. data/spec/outputs/s3/temporary_file_spec.rb +47 -0
  79. data/spec/outputs/s3/time_rotation_policy_spec.rb +60 -0
  80. data/spec/outputs/s3/uploader_spec.rb +69 -0
  81. data/spec/outputs/s3/writable_directory_validator_spec.rb +40 -0
  82. data/spec/outputs/s3/write_bucket_permission_validator_spec.rb +49 -0
  83. data/spec/outputs/s3_spec.rb +232 -0
  84. data/spec/outputs/sns_spec.rb +160 -0
  85. data/spec/plugin_mixin/aws_config_spec.rb +217 -0
  86. data/spec/spec_helper.rb +8 -0
  87. data/spec/support/helpers.rb +119 -0
  88. data/spec/unit/outputs/sqs_spec.rb +247 -0
  89. metadata +467 -0
@@ -0,0 +1,167 @@
1
+ # encoding: utf-8
2
+
3
+ require 'aws-sdk-sqs'
4
+ require 'logstash/errors'
5
+ require 'logstash/namespace'
6
+ require 'logstash/outputs/base'
7
+ require 'logstash/plugin_mixins/aws_config'
8
+
9
+ # Push events to an Amazon Web Services (AWS) Simple Queue Service (SQS) queue.
10
+ #
11
+ # SQS is a simple, scalable queue system that is part of the Amazon Web
12
+ # Services suite of tools. Although SQS is similar to other queuing systems
13
+ # such as Advanced Message Queuing Protocol (AMQP), it uses a custom API and
14
+ # requires that you have an AWS account. See http://aws.amazon.com/sqs/ for
15
+ # more details on how SQS works, what the pricing schedule looks like and how
16
+ # to setup a queue.
17
+ #
18
+ # The "consumer" identity must have the following permissions on the queue:
19
+ #
20
+ # * `sqs:GetQueueUrl`
21
+ # * `sqs:SendMessage`
22
+ # * `sqs:SendMessageBatch`
23
+ #
24
+ # Typically, you should setup an IAM policy, create a user and apply the IAM
25
+ # policy to the user. See http://aws.amazon.com/iam/ for more details on
26
+ # setting up AWS identities. A sample policy is as follows:
27
+ #
28
+ # [source,json]
29
+ # {
30
+ # "Version": "2012-10-17",
31
+ # "Statement": [
32
+ # {
33
+ # "Effect": "Allow",
34
+ # "Action": [
35
+ # "sqs:GetQueueUrl",
36
+ # "sqs:SendMessage",
37
+ # "sqs:SendMessageBatch"
38
+ # ],
39
+ # "Resource": "arn:aws:sqs:us-east-1:123456789012:my-sqs-queue"
40
+ # }
41
+ # ]
42
+ # }
43
+ #
44
+ # ==== Batch Publishing
45
+ # This output publishes messages to SQS in batches in order to optimize event
46
+ # throughput and increase performance. This is done using the
47
+ # [`SendMessageBatch`](http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessageBatch.html)
48
+ # API. When publishing messages to SQS in batches, the following service limits
49
+ # must be respected (see
50
+ # [Limits in Amazon SQS](http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/limits-messages.html)):
51
+ #
52
+ # * The maximum allowed individual message size is 256KiB.
53
+ # * The maximum total payload size (i.e. the sum of the sizes of all
54
+ # individual messages within a batch) is also 256KiB.
55
+ #
56
+ # This plugin will dynamically adjust the size of the batch published to SQS in
57
+ # order to ensure that the total payload size does not exceed 256KiB.
58
+ #
59
+ # WARNING: This output cannot currently handle messages larger than 256KiB. Any
60
+ # single message exceeding this size will be dropped.
61
+ #
62
+ class LogStash::Outputs::SQS < LogStash::Outputs::Base
63
+ include LogStash::PluginMixins::AwsConfig::V2
64
+
65
+ config_name 'sqs'
66
+ default :codec, 'json'
67
+
68
+ concurrency :shared
69
+
70
+ # The number of events to be sent in each batch. Set this to `1` to disable
71
+ # the batch sending of messages.
72
+ config :batch_events, :validate => :number, :default => 10
73
+
74
+ # The maximum number of bytes for any message sent to SQS. Messages exceeding
75
+ # this size will be dropped. See
76
+ # http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/limits-messages.html.
77
+ config :message_max_size, :validate => :bytes, :default => '256KiB'
78
+
79
+ # The name of the target SQS queue. Note that this is just the name of the
80
+ # queue, not the URL or ARN.
81
+ config :queue, :validate => :string, :required => true
82
+
83
+ # Account ID of the AWS account which owns the queue. Note IAM permissions
84
+ # need to be configured on both accounts to function.
85
+ config :queue_owner_aws_account_id, :validate => :string, :required => false
86
+
87
+ public
88
+ def register
89
+ @sqs = Aws::SQS::Client.new(aws_options_hash)
90
+
91
+ if @batch_events > 10
92
+ raise LogStash::ConfigurationError, 'The maximum batch size is 10 events'
93
+ elsif @batch_events < 1
94
+ raise LogStash::ConfigurationError, 'The batch size must be greater than 0'
95
+ end
96
+
97
+ begin
98
+ params = { queue_name: @queue }
99
+ params[:queue_owner_aws_account_id] = @queue_owner_aws_account_id if @queue_owner_aws_account_id
100
+
101
+ @logger.debug('Connecting to SQS queue', params.merge(region: region))
102
+ @queue_url = @sqs.get_queue_url(params)[:queue_url]
103
+ @logger.info('Connected to SQS queue successfully', params.merge(region: region))
104
+ rescue Aws::SQS::Errors::ServiceError => e
105
+ @logger.error('Failed to connect to SQS', :error => e)
106
+ raise LogStash::ConfigurationError, 'Verify the SQS queue name and your credentials'
107
+ end
108
+ end
109
+
110
+ public
111
+ def multi_receive_encoded(encoded_events)
112
+ if @batch_events > 1
113
+ multi_receive_encoded_batch(encoded_events)
114
+ else
115
+ multi_receive_encoded_single(encoded_events)
116
+ end
117
+ end
118
+
119
+ private
120
+ def multi_receive_encoded_batch(encoded_events)
121
+ bytes = 0
122
+ entries = []
123
+
124
+ # Split the events into multiple batches to ensure that no single batch
125
+ # exceeds `@message_max_size` bytes.
126
+ encoded_events.each_with_index do |encoded_event, index|
127
+ event, encoded = encoded_event
128
+
129
+ if encoded.bytesize > @message_max_size
130
+ @logger.warn('Message exceeds maximum length and will be dropped', :message_size => encoded.bytesize)
131
+ next
132
+ end
133
+
134
+ if entries.size >= @batch_events or (bytes + encoded.bytesize) > @message_max_size
135
+ send_message_batch(entries)
136
+
137
+ bytes = 0
138
+ entries = []
139
+ end
140
+
141
+ bytes += encoded.bytesize
142
+ entries.push(:id => index.to_s, :message_body => encoded)
143
+ end
144
+
145
+ send_message_batch(entries) unless entries.empty?
146
+ end
147
+
148
+ private
149
+ def multi_receive_encoded_single(encoded_events)
150
+ encoded_events.each do |encoded_event|
151
+ event, encoded = encoded_event
152
+
153
+ if encoded.bytesize > @message_max_size
154
+ @logger.warn('Message exceeds maximum length and will be dropped', :message_size => encoded.bytesize)
155
+ next
156
+ end
157
+
158
+ @sqs.send_message(:queue_url => @queue_url, :message_body => encoded)
159
+ end
160
+ end
161
+
162
+ private
163
+ def send_message_batch(entries)
164
+ @logger.debug("Publishing #{entries.size} messages to SQS", :queue_url => @queue_url, :entries => entries)
165
+ @sqs.send_message_batch(:queue_url => @queue_url, :entries => entries)
166
+ end
167
+ end
@@ -0,0 +1,54 @@
1
+ module LogStash::PluginMixins::AwsConfig::Generic
2
+ def self.included(base)
3
+ base.extend(self)
4
+ base.generic_aws_config
5
+ end
6
+
7
+ def generic_aws_config
8
+ # The AWS Region
9
+ config :region, :validate => :string, :default => LogStash::PluginMixins::AwsConfig::US_EAST_1
10
+
11
+ # This plugin uses the AWS SDK and supports several ways to get credentials, which will be tried in this order:
12
+ #
13
+ # 1. Static configuration, using `access_key_id` and `secret_access_key` params in the logstash plugin config
14
+ # 2. External credentials file specified by `aws_credentials_file`
15
+ # 3. Environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`
16
+ # 4. Environment variables `AMAZON_ACCESS_KEY_ID` and `AMAZON_SECRET_ACCESS_KEY`
17
+ # 5. IAM Instance Profile (available when running inside EC2)
18
+ config :access_key_id, :validate => :string
19
+
20
+ # The AWS Secret Access Key
21
+ config :secret_access_key, :validate => :password
22
+
23
+ # The AWS Session token for temporary credential
24
+ config :session_token, :validate => :password
25
+
26
+ # URI to proxy server if required
27
+ config :proxy_uri, :validate => :string
28
+
29
+ # Custom endpoint to connect to s3
30
+ config :endpoint, :validate => :string
31
+
32
+ # The AWS IAM Role to assume, if any.
33
+ # This is used to generate temporary credentials typically for cross-account access.
34
+ # See https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html for more information.
35
+ # When `role_arn` is set, AWS (`access_key_id`/`secret_access_key`) credentials still get used if they're configured.
36
+ config :role_arn, :validate => :string
37
+
38
+ # Session name to use when assuming an IAM role
39
+ config :role_session_name, :validate => :string, :default => "logstash"
40
+
41
+ # Path to YAML file containing a hash of AWS credentials.
42
+ # This file will only be loaded if `access_key_id` and
43
+ # `secret_access_key` aren't set. The contents of the
44
+ # file should look like this:
45
+ #
46
+ # [source,ruby]
47
+ # ----------------------------------
48
+ # :access_key_id: "12345"
49
+ # :secret_access_key: "54321"
50
+ # ----------------------------------
51
+ #
52
+ config :aws_credentials_file, :validate => :string
53
+ end
54
+ end
@@ -0,0 +1,93 @@
1
+ # encoding: utf-8
2
+ require "logstash/plugin_mixins/aws_config/generic"
3
+
4
+ module LogStash::PluginMixins::AwsConfig::V2
5
+ def self.included(base)
6
+ base.extend(self)
7
+ base.send(:include, LogStash::PluginMixins::AwsConfig::Generic)
8
+ end
9
+
10
+ public
11
+ def aws_options_hash
12
+ opts = {}
13
+
14
+ opts[:http_proxy] = @proxy_uri if @proxy_uri
15
+
16
+ if @role_arn
17
+ credentials = assume_role(opts.dup)
18
+ opts[:credentials] = credentials
19
+ else
20
+ credentials = aws_credentials
21
+ opts[:credentials] = credentials if credentials
22
+ end
23
+
24
+ if self.respond_to?(:aws_service_endpoint)
25
+ # used by CloudWatch to basically do the same as bellow (returns { region: region })
26
+ opts.merge!(self.aws_service_endpoint(@region))
27
+ else
28
+ # NOTE: setting :region works with the aws sdk (resolves correct endpoint)
29
+ opts[:region] = @region
30
+ end
31
+
32
+ opts[:endpoint] = @endpoint unless @endpoint.nil?
33
+
34
+ if respond_to?(:additional_settings)
35
+ opts = symbolize_keys_and_cast_true_false(additional_settings).merge(opts)
36
+ end
37
+
38
+ return opts
39
+ end
40
+
41
+ private
42
+
43
+ def aws_credentials
44
+ if @access_key_id && @secret_access_key
45
+ Aws::Credentials.new(@access_key_id, @secret_access_key.value, @session_token ? @session_token.value : nil)
46
+ elsif @access_key_id.nil? ^ @secret_access_key.nil?
47
+ @logger.warn("Likely config error: Only one of access_key_id or secret_access_key was provided but not both.")
48
+ secret_access_key = @secret_access_key ? @secret_access_key.value : nil
49
+ Aws::Credentials.new(@access_key_id, secret_access_key, @session_token ? @session_token.value : nil)
50
+ elsif @aws_credentials_file
51
+ credentials_opts = YAML.load_file(@aws_credentials_file)
52
+ credentials_opts.default_proc = lambda { |hash, key| hash.fetch(key.to_s, nil) }
53
+ Aws::Credentials.new(credentials_opts[:access_key_id],
54
+ credentials_opts[:secret_access_key],
55
+ credentials_opts[:session_token])
56
+ else
57
+ nil # AWS client will read ENV or ~/.aws/credentials
58
+ end
59
+ end
60
+ alias credentials aws_credentials
61
+
62
+ def assume_role(opts = {})
63
+ unless opts.key?(:credentials)
64
+ credentials = aws_credentials
65
+ opts[:credentials] = credentials if credentials
66
+ end
67
+
68
+ # for a regional endpoint :region is always required by AWS
69
+ opts[:region] = @region
70
+
71
+ Aws::AssumeRoleCredentials.new(
72
+ :client => Aws::STS::Client.new(opts),
73
+ :role_arn => @role_arn,
74
+ :role_session_name => @role_session_name
75
+ )
76
+ end
77
+
78
+ def symbolize_keys_and_cast_true_false(hash)
79
+ case hash
80
+ when Hash
81
+ symbolized = {}
82
+ hash.each { |key, value| symbolized[key.to_sym] = symbolize_keys_and_cast_true_false(value) }
83
+ symbolized
84
+ when 'true'
85
+ true
86
+ when 'false'
87
+ false
88
+ else
89
+ hash
90
+ end
91
+ end
92
+
93
+ end
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+ require "logstash/config/mixin"
3
+
4
+ module LogStash::PluginMixins::AwsConfig
5
+ require "logstash/plugin_mixins/aws_config/v2"
6
+
7
+ US_EAST_1 = "us-east-1"
8
+ end
@@ -0,0 +1,52 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "logstash-integration-aws"
3
+ s.version = "0.1.0.pre"
4
+ s.licenses = ["Apache-2.0"]
5
+ s.summary = "Collection of Logstash plugins that integrate with AWS"
6
+ s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
7
+ s.authors = ["Elastic"]
8
+ s.email = "info@elastic.co"
9
+ s.homepage = "http://www.elastic.co/guide/en/logstash/current/index.html"
10
+ s.metadata = {
11
+ "logstash_plugin" => "true",
12
+ "logstash_group" => "integration",
13
+ "integration_plugins" => %w(
14
+ logstash-codec-cloudfront
15
+ logstash-codec-cloudtrail
16
+ logstash-input-cloudwatch
17
+ logstash-input-s3
18
+ logstash-input-sqs
19
+ logstash-mixin-aws
20
+ logstash-output-cloudwatch
21
+ logstash-output-s3
22
+ logstash-output-sns
23
+ logstash-output-sqs).join(",")
24
+ }
25
+
26
+
27
+ s.require_paths = ["lib"]
28
+ s.files = Dir["lib/**/*","spec/**/*","*.gemspec","*.md","CONTRIBUTORS","Gemfile","LICENSE","NOTICE.TXT", "VERSION", "docs/**/*"]
29
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
30
+
31
+ s.add_runtime_dependency "logstash-core-plugin-api", ">= 2.1.12", "<= 2.99"
32
+ s.add_runtime_dependency "concurrent-ruby"
33
+ s.add_runtime_dependency "logstash-codec-json"
34
+ s.add_runtime_dependency "logstash-codec-plain"
35
+ s.add_runtime_dependency "rufus-scheduler", ">= 3.0.9"
36
+ s.add_runtime_dependency "stud", "~> 0.0.22"
37
+ s.add_runtime_dependency "aws-sdk-core", "~> 3"
38
+ s.add_runtime_dependency "aws-sdk-s3"
39
+ s.add_runtime_dependency "aws-sdk-sqs"
40
+ s.add_runtime_dependency "aws-sdk-sns"
41
+ s.add_runtime_dependency "aws-sdk-cloudwatch"
42
+ s.add_runtime_dependency "aws-sdk-cloudfront"
43
+ s.add_runtime_dependency "aws-sdk-resourcegroups"
44
+
45
+ s.add_development_dependency "logstash-codec-json_lines"
46
+ s.add_development_dependency "logstash-codec-multiline"
47
+ s.add_development_dependency "logstash-codec-json"
48
+ s.add_development_dependency "logstash-codec-line"
49
+ s.add_development_dependency "logstash-devutils"
50
+ s.add_development_dependency "logstash-input-generator"
51
+ s.add_development_dependency "timecop"
52
+ end
@@ -0,0 +1,92 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/codecs/cloudfront"
4
+ require "logstash/errors"
5
+ require "stringio"
6
+ require "zlib"
7
+
8
+ def compress_with_gzip(io)
9
+ compressed = StringIO.new('', 'r+b')
10
+
11
+ gzip = Zlib::GzipWriter.new(compressed)
12
+ gzip.write(io.read)
13
+ gzip.finish
14
+
15
+ compressed.rewind
16
+
17
+ compressed
18
+ end
19
+
20
+ describe LogStash::Codecs::Cloudfront do
21
+ let!(:uncompressed_cloudfront_log) do
22
+ # Using format from
23
+ # http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html
24
+ str = StringIO.new
25
+
26
+ str << "#Version: 1.0\n"
27
+ str << "#Fields: date time x-edge-location c-ip x-event sc-bytes x-cf-status x-cf-client-id cs-uri-stem cs-uri-query c-referrer x-page-url​ c-user-agent x-sname x-sname-query x-file-ext x-sid\n"
28
+ str << "2010-03-12 23:51:20 SEA4 192.0.2.147 connect 2014 OK bfd8a98bee0840d9b871b7f6ade9908f rtmp://shqshne4jdp4b6.cloudfront.net/cfx/st​ key=value http://player.longtailvideo.com/player.swf http://www.longtailvideo.com/support/jw-player-setup-wizard?example=204 LNX%2010,0,32,18 - - - -\n"
29
+ str << "2010-03-12 23:51:21 SEA4 192.0.2.222 play 3914 OK bfd8a98bee0840d9b871b7f6ade9908f rtmp://shqshne4jdp4b6.cloudfront.net/cfx/st​ key=value http://player.longtailvideo.com/player.swf http://www.longtailvideo.com/support/jw-player-setup-wizard?example=204 LNX%2010,0,32,18 myvideo p=2&q=4 flv 1\n"
30
+
31
+ str.rewind
32
+ str
33
+ end
34
+
35
+ describe "#decode" do
36
+ it "should create events from a gzip file" do
37
+ events = []
38
+
39
+ subject.decode(compress_with_gzip(uncompressed_cloudfront_log)) do |event|
40
+ events << event
41
+ end
42
+
43
+ expect(events.size).to eq(2)
44
+ end
45
+
46
+ it 'should extract the metadata of the file' do
47
+ events = []
48
+
49
+ subject.decode(compress_with_gzip(uncompressed_cloudfront_log)) do |event|
50
+ events << event
51
+ end
52
+
53
+ expect(events.first.get("cloudfront_version")).to eq("1.0")
54
+ expect(events.first.get("cloudfront_fields")).to eq("date time x-edge-location c-ip x-event sc-bytes x-cf-status x-cf-client-id cs-uri-stem cs-uri-query c-referrer x-page-url​ c-user-agent x-sname x-sname-query x-file-ext x-sid")
55
+ end
56
+ end
57
+
58
+ describe "#extract_version" do
59
+ it "returns the version from a matched string" do
60
+ line = "#Version: 1.0"
61
+
62
+ expect(subject.extract_version(line)).to eq("1.0")
63
+ end
64
+
65
+ it "doesn't return anything if version isnt matched" do
66
+ line = "Bleh my string"
67
+ expect(subject.extract_version(line)).to eq(nil)
68
+ end
69
+
70
+ it "doesn't match if #Version is not at the beginning of the string" do
71
+ line = "2010-03-12 23:53:44 SEA4 192.0.2.4 stop 323914 OK bfd8a98bee0840d9b871b7f6ade9908f #Version: 1.0 Bleh blah"
72
+ expect(subject.extract_version(line)).to eq(nil)
73
+ end
74
+ end
75
+
76
+ describe "#extract_fields" do
77
+ it "return a string with all the fields" do
78
+ line = "#Fields: date time x-edge-location c-ip x-event sc-bytes x-cf-status x-cf-client-id cs-uri-stem cs-uri-query c-referrer x-page-url​ c-user-agent x-sname x-sname-query x-file-ext x-sid"
79
+ expect(subject.extract_fields(line)).to eq("date time x-edge-location c-ip x-event sc-bytes x-cf-status x-cf-client-id cs-uri-stem cs-uri-query c-referrer x-page-url​ c-user-agent x-sname x-sname-query x-file-ext x-sid")
80
+ end
81
+
82
+ it "doesn't return anything if we can the fields list" do
83
+ line = "Bleh my string"
84
+ expect(subject.extract_fields(line)).to eq(nil)
85
+ end
86
+
87
+ it "doesnt match if #Fields: is not at the beginning of the string" do
88
+ line = "2010-03-12 23:53:44 SEA4 192.0.2.4 stop 323914 OK bfd8a98bee0840d9b871b7f6ade9908f #Fields: 1.0 Bleh blah"
89
+ expect(subject.extract_fields(line)).to eq(nil)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,56 @@
1
+ require "logstash/devutils/rspec/spec_helper"
2
+ require "logstash/plugin"
3
+ require "logstash/codecs/cloudtrail"
4
+ require 'resolv'
5
+
6
+ describe LogStash::Codecs::CloudTrail do
7
+
8
+ shared_examples_for "it handles valid ip addresses" do
9
+ it 'should pass through valid ip addresses' do
10
+ ip_addresses.each do |valid_ip_address|
11
+ subject.decode("{\"Records\":[{\"sourceIpAddress\":\"#{valid_ip_address}\"}]}") do |event|
12
+ expect(event.get("sourceIpAddress")).to eq(valid_ip_address)
13
+ expect(event.get("sourceHost")).to be_nil
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ describe '#decode' do
20
+ it 'accepts data without a Records property' do
21
+ expect { |b|
22
+ subject.decode('{}', &b)
23
+ }.not_to yield_control
24
+ end
25
+
26
+ it 'accepts records with null requestParameters' do
27
+ expect { |b|
28
+ subject.decode('{"Records":[{"requestParameters":null}]}', &b)
29
+ }.to yield_control
30
+ end
31
+
32
+ context 'with ipv4 sourceIpAddress values' do
33
+ let(:ip_addresses) { ["127.0.0.1", "8.8.8.8", "10.10.10.10", "100.100.100.100", "1.12.123.234"] }
34
+ it_behaves_like 'it handles valid ip addresses'
35
+ end
36
+
37
+ context 'with ipv6 sourceIpAddress values' do
38
+ let(:ip_addresses) { ["2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:db8:85a3::8a2e:370:7334", "::1", "::"] }
39
+ it_behaves_like 'it handles valid ip addresses'
40
+ end
41
+
42
+ it 'accepts records with an invalid sourceIpAddress' do
43
+ subject.decode('{"Records":[{"sourceIpAddress":"www.elastic.co"}]}') do |event|
44
+ expect(event.get("sourceIpAddress")).to be_nil
45
+ expect(event.get("sourceHost")).to eq("www.elastic.co")
46
+ end
47
+ end
48
+
49
+ it 'accepts records with a no sourceIpAddress' do
50
+ subject.decode('{"Records":[{"sourceIpAddress":null}]}') do |event|
51
+ expect(event.get("sourceIpAddress")).to be_nil
52
+ expect(event.get("sourceHost")).to be_nil
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,2 @@
1
+ :access_key_id: '1234'
2
+ :secret_access_key: secret
@@ -0,0 +1,3 @@
1
+ :access_key_id: '1234'
2
+ :secret_access_key: secret
3
+ :session_token: session_token
@@ -0,0 +1,4 @@
1
+ #Version: 1.0
2
+ #Fields: date time x-edge-location c-ip x-event sc-bytes x-cf-status x-cf-client-id cs-uri-stem cs-uri-query c-referrer x-page-url​ c-user-agent x-sname x-sname-query x-file-ext x-sid
3
+ 2010-03-12 23:51:20 SEA4 192.0.2.147 connect 2014 OK bfd8a98bee0840d9b871b7f6ade9908f rtmp://shqshne4jdp4b6.cloudfront.net/cfx/st​ key=value http://player.longtailvideo.com/player.swf http://www.longtailvideo.com/support/jw-player-setup-wizard?example=204 LNX%2010,0,32,18 - - - -
4
+ 2010-03-12 23:51:21 SEA4 192.0.2.222 play 3914 OK bfd8a98bee0840d9b871b7f6ade9908f rtmp://shqshne4jdp4b6.cloudfront.net/cfx/st​ key=value http://player.longtailvideo.com/player.swf http://www.longtailvideo.com/support/jw-player-setup-wizard?example=204 LNX%2010,0,32,18 myvideo p=2&q=4 flv 1
Binary file
Binary file
@@ -0,0 +1,2 @@
1
+ 2015-01-01T02:52:45.866722Z no "GET http://www.logstash.com:80/utfmadness/��4od HTTP/1.1"
2
+
@@ -0,0 +1,2 @@
1
+ { "hello": "world" }
2
+ { "hello": "awesome world" }
@@ -0,0 +1,2 @@
1
+ { "message": ["GET", 32, "/health"] }
2
+ { "message": true }
@@ -0,0 +1,6 @@
1
+ __SEPARATOR__
2
+ file:1 record:1 line:1
3
+ file:1 record:1 line:2
4
+ __SEPARATOR__
5
+ file:1 record:2 line:1
6
+ file:1 record:2 line:2
@@ -0,0 +1,2 @@
1
+ 2010-03-12 23:51:20 SEA4 192.0.2.147 connect 2014 OK bfd8a98bee0840d9b871b7f6ade9908f rtmp://shqshne4jdp4b6.cloudfront.net/cfx/st​ key=value http://player.longtailvideo.com/player.swf http://www.longtailvideo.com/support/jw-player-setup-wizard?example=204 LNX%2010,0,32,18 - - - -
2
+ 2010-03-12 23:51:21 SEA4 192.0.2.222 play 3914 OK bfd8a98bee0840d9b871b7f6ade9908f rtmp://shqshne4jdp4b6.cloudfront.net/cfx/st​ key=value http://player.longtailvideo.com/player.swf http://www.longtailvideo.com/support/jw-player-setup-wizard?example=204 LNX%2010,0,32,18 myvideo p=2&q=4 flv 1
@@ -0,0 +1,85 @@
1
+ require 'logstash/devutils/rspec/spec_helper'
2
+ require 'logstash/devutils/rspec/shared_examples'
3
+ require 'logstash/inputs/cloudwatch'
4
+
5
+ describe LogStash::Inputs::CloudWatch do
6
+ subject { LogStash::Inputs::CloudWatch.new(config) }
7
+ let(:config) {
8
+ {
9
+ 'access_key_id' => '1234',
10
+ 'secret_access_key' => 'secret',
11
+ 'metrics' => [ 'CPUUtilization' ],
12
+ 'region' => 'us-east-1'
13
+ }
14
+ }
15
+
16
+
17
+ before do
18
+ Aws.config[:stub_responses] = true
19
+ Thread.abort_on_exception = true
20
+ end
21
+
22
+ shared_examples_for 'it requires filters' do
23
+ context 'without filters' do
24
+ it "raises an error" do
25
+ expect { subject.register }.to raise_error(StandardError)
26
+ end
27
+ end
28
+
29
+ context 'with filters' do
30
+ let (:config) { super().merge('filters' => { 'tag:Monitoring' => 'Yes' })}
31
+
32
+ it "registers succesfully" do
33
+ expect { subject.register }.to_not raise_error
34
+ end
35
+ end
36
+ end
37
+
38
+ shared_examples_for 'it does not require filters' do
39
+ context 'without filters' do
40
+ it "registers succesfully" do
41
+ expect { subject.register }.to_not raise_error
42
+ end
43
+ end
44
+
45
+ context 'with filters' do
46
+ let (:config) { super().merge('filters' => { 'tag:Monitoring' => 'Yes' })}
47
+
48
+ it "registers succesfully" do
49
+ expect { subject.register }.to_not raise_error
50
+ end
51
+ end
52
+ end
53
+
54
+ describe 'shutdown' do
55
+ let(:metrics) { double("metrics") }
56
+ let(:config) { super().merge('namespace' => 'AWS/EC2') }
57
+
58
+ before do
59
+ allow(subject).to receive(:metrics_for).and_return(metrics)
60
+ allow(metrics).to receive(:count).and_return(1)
61
+ allow(metrics).to receive(:each).and_return(['DiskWriteBytes'])
62
+ end
63
+
64
+ it_behaves_like "an interruptible input plugin"
65
+ end
66
+
67
+ describe '#register' do
68
+
69
+ context "EC2 namespace" do
70
+ let(:config) { super().merge('namespace' => 'AWS/EC2') }
71
+ it_behaves_like 'it does not require filters'
72
+ end
73
+
74
+ context "EBS namespace" do
75
+ let(:config) { super().merge('namespace' => 'AWS/EBS') }
76
+ it_behaves_like 'it requires filters'
77
+ end
78
+
79
+ context "RDS namespace" do
80
+ let(:config) { super().merge('namespace' => 'AWS/RDS') }
81
+ it_behaves_like 'it requires filters'
82
+ end
83
+
84
+ end
85
+ end