logstash-output-sns 1.0.0 → 2.0.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
  SHA1:
3
- metadata.gz: 5104315bfaa6592b7a01af7f60e91f2fbf01033b
4
- data.tar.gz: b26ecbe7cf8da6ec5f7c67cd2c281c259bd6d4fc
3
+ metadata.gz: 6b4ac99bfe2f2202031197d1244322ea5922043b
4
+ data.tar.gz: 3708b40a61096f6f3f26a0e89126172812ad7d93
5
5
  SHA512:
6
- metadata.gz: 141bd61ec417b7d05663248698f90d08a6a96d39c5557d26ed0ac33e0a89ad140fcede305c80654f1b0b9c2b3bcf0db375ac9105b664d3593a15b84a41a5ccd9
7
- data.tar.gz: 2a249724c9182a327ed908dd435e252ca6c4ae6cea1088aba51826b848e5a0807c6946937aca2912dcbad2c3b72e7afe0f3fa77d0aeed1cd8ff4a35bbeb959a1
6
+ metadata.gz: 3c5f526f7fd22744a1eed1e441b9a1c3ad505d5fbad019155bac79224c80c8c153216462bb96259fcc76aca4a5d0c9beb2a6819bfdd9c5f6e39c4624c6fdffa2
7
+ data.tar.gz: 4c27f467d148a9575a79f0390e0958e2948890a1e4b9f98c54bd0ec8d8f33b63599014600516c2cb926287ce413b8904d2c34f8211c13dc6aacae292f3b1b862
data/CHANGELOG.md CHANGED
@@ -0,0 +1,6 @@
1
+ # 1.0.0
2
+ * Full refactor.
3
+ * This plugin now uses codecs for all formatting. The 'format' option has now been removed. Please use a codec.
4
+ # 0.1.5
5
+ * If no `subject` are specified fallback to the %{host} key (https://github.com/logstash-plugins/logstash-output-sns/pull/2)
6
+ * Migrate the SNS Api to use the AWS-SDK v2
data/Gemfile CHANGED
@@ -1,2 +1,2 @@
1
1
  source 'https://rubygems.org'
2
- gemspec
2
+ gemspec
@@ -6,7 +6,7 @@ require "logstash/plugin_mixins/aws_config"
6
6
  # SNS output.
7
7
  #
8
8
  # Send events to Amazon's Simple Notification Service, a hosted pub/sub
9
- # framework. It supports subscribers of type email, HTTP/S, SMS, and SQS.
9
+ # framework. It supports various subscription types, including email, HTTP/S, SMS, and SQS.
10
10
  #
11
11
  # For further documentation about the service see:
12
12
  #
@@ -17,25 +17,23 @@ require "logstash/plugin_mixins/aws_config"
17
17
  # * `sns` - If no ARN is found in the configuration file, this will be used as
18
18
  # the ARN to publish.
19
19
  # * `sns_subject` - The subject line that should be used.
20
- # Optional. The "%{host}" will be used if not present and truncated at
21
- # `MAX_SUBJECT_SIZE_IN_CHARACTERS`.
22
- # * `sns_message` - The message that should be
23
- # sent. Optional. The event serialzed as JSON will be used if not present and
20
+ # Optional. The "%{host}" will be used if `sns_subject` is not present. The subject
21
+ # will be truncated to 100 characters. If `sns_subject` is set to a non-string value a JSON version of that value will be saved.
22
+ # * `sns_message` - Optional string of message to be sent. If this is set to a non-string value it will be encoded with the specified `codec`. If this is not set the entire event will be encoded with the codec.
24
23
  # with the @message truncated so that the length of the JSON fits in
25
- # `MAX_MESSAGE_SIZE_IN_BYTES`.
24
+ # `32768` bytes.
26
25
  #
27
26
  class LogStash::Outputs::Sns < LogStash::Outputs::Base
28
- include LogStash::PluginMixins::AwsConfig
27
+ include LogStash::PluginMixins::AwsConfig::V2
29
28
 
30
29
  MAX_SUBJECT_SIZE_IN_CHARACTERS = 100
31
30
  MAX_MESSAGE_SIZE_IN_BYTES = 32768
31
+ NO_SUBJECT = "NO SUBJECT"
32
32
 
33
33
  config_name "sns"
34
34
 
35
- # Message format. Defaults to plain text.
36
- config :format, :validate => [ "json", "plain" ], :default => "plain"
37
-
38
- # SNS topic ARN.
35
+ # Optional ARN to send messages to. If you do not set this you must
36
+ # include the `sns` field in your events to set the ARN on a per-message basis!
39
37
  config :arn, :validate => :string
40
38
 
41
39
  # When an ARN for an SNS topic is specified here, the message
@@ -46,23 +44,16 @@ class LogStash::Outputs::Sns < LogStash::Outputs::Base
46
44
  #
47
45
  config :publish_boot_message_arn, :validate => :string
48
46
 
49
- public
50
- def aws_service_endpoint(region)
51
- return {
52
- :sns_endpoint => "sns.#{region}.amazonaws.com"
53
- }
54
- end
55
-
56
47
  public
57
48
  def register
58
- require "aws-sdk"
49
+ require "aws-sdk-resources"
59
50
 
60
- @sns = AWS::SNS.new(aws_options_hash)
51
+ @sns = Aws::SNS::Client.new(aws_options_hash)
61
52
 
62
- # Try to publish a "Logstash booted" message to the ARN provided to
63
- # cause an error ASAP if the credentials are bad.
64
- if @publish_boot_message_arn
65
- @sns.topics[@publish_boot_message_arn].publish("Logstash successfully booted", :subject => "Logstash booted")
53
+ publish_boot_message_arn()
54
+
55
+ @codec.on_event do |event, encoded|
56
+ send_sns_message(event_arn(event), event_subject(event), encoded)
66
57
  end
67
58
  end
68
59
 
@@ -70,54 +61,58 @@ class LogStash::Outputs::Sns < LogStash::Outputs::Base
70
61
  def receive(event)
71
62
  return unless output?(event)
72
63
 
73
- arn = Array(event["sns"]).first || @arn
74
-
75
- raise "An SNS ARN required." unless arn
76
-
77
- message = Array(event["sns_message"]).first
78
- subject = Array(event["sns_subject"]).first || event["host"]
79
-
80
- # Ensure message doesn't exceed the maximum size.
81
- if message
82
- # TODO: Utilize `byteslice` in JRuby 1.7: http://jira.codehaus.org/browse/JRUBY-5547
83
- message = message.slice(0, MAX_MESSAGE_SIZE_IN_BYTES)
84
- else
85
- if @format == "plain"
86
- message = self.class.format_message(event)
64
+ if (sns_msg = event["sns_message"])
65
+ if sns_msg.is_a?(String)
66
+ send_sns_message(event_arn(event), event_subject(event), sns_msg)
87
67
  else
88
- message = self.class.json_message(event)
68
+ @codec.encode(sns_msg)
89
69
  end
70
+ else
71
+ @codec.encode(event)
90
72
  end
73
+ end
91
74
 
92
- # Log event.
93
- @logger.debug("Sending event to SNS topic [#{arn}] with subject [#{subject}] and message:")
94
- message.split("\n").each { |line| @logger.debug(line) }
95
-
96
- # Publish the message.
97
- @sns.topics[arn].publish(message, :subject => subject.slice(0, MAX_SUBJECT_SIZE_IN_CHARACTERS))
75
+ private
76
+ def publish_boot_message_arn
77
+ # Try to publish a "Logstash booted" message to the ARN provided to
78
+ # cause an error ASAP if the credentials are bad.
79
+ if @publish_boot_message_arn
80
+ send_sns_message(@publish_boot_message_arn, 'Logstash booted', 'Logstash successfully booted')
81
+ end
98
82
  end
99
83
 
100
- def self.json_message(event)
101
- json = event.to_json
102
- json_size = json.bytesize
84
+ private
85
+ def send_sns_message(arn, subject, message)
86
+ raise ArgumentError, 'An SNS ARN is required.' unless arn
103
87
 
104
- # Truncate only the message if the JSON structure is too large.
105
- if json_size > MAX_MESSAGE_SIZE_IN_BYTES
106
- # TODO: Utilize `byteslice` in JRuby 1.7: http://jira.codehaus.org/browse/JRUBY-5547
107
- event["message"] = event["message"].slice(0, (event["message"].bytesize - (json_size - MAX_MESSAGE_SIZE_IN_BYTES)))
108
- end
88
+ trunc_subj = subject.slice(0, MAX_SUBJECT_SIZE_IN_CHARACTERS)
89
+ trunc_msg = message.slice(0, MAX_MESSAGE_SIZE_IN_BYTES)
90
+
91
+ @logger.debug? && @logger.debug("Sending event to SNS topic [#{arn}] with subject [#{trunc_subj}] and message: #{trunc_msg}")
109
92
 
110
- event.to_json
93
+ @sns.publish({
94
+ :topic_arn => arn,
95
+ :subject => trunc_subj,
96
+ :message => trunc_msg
97
+ })
111
98
  end
112
99
 
113
- def self.format_message(event)
114
- message = "Date: #{event.timestamp}\n"
115
- message << "Source: #{event["source"]}\n"
116
- message << "Tags: #{(event["tags"] || []).join(', ')}\n"
117
- message << "Fields: #{event.to_hash.inspect}\n"
118
- message << "Message: #{event["message"]}"
100
+ private
101
+ def event_subject(event)
102
+ sns_subject = event["sns_subject"]
103
+ if sns_subject.is_a?(String)
104
+ sns_subject
105
+ elsif sns_subject
106
+ LogStash::Json.dump(sns_subject)
107
+ elsif event["host"]
108
+ event["host"]
109
+ else
110
+ NO_SUBJECT
111
+ end
112
+ end
119
113
 
120
- # TODO: Utilize `byteslice` in JRuby 1.7: http://jira.codehaus.org/browse/JRUBY-5547
121
- message.slice(0, MAX_MESSAGE_SIZE_IN_BYTES)
114
+ private
115
+ def event_arn(event)
116
+ event["sns"] || @arn
122
117
  end
123
118
  end
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-output-sns'
4
- s.version = '1.0.0'
4
+ s.version = '2.0.0'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Send events to Amazon's Simple Notification Service a hosted pub/sub framework"
7
7
  s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
@@ -21,9 +21,7 @@ Gem::Specification.new do |s|
21
21
 
22
22
  # Gem dependencies
23
23
  s.add_runtime_dependency "logstash-core", '>= 1.4.0', '< 2.0.0'
24
- s.add_runtime_dependency 'logstash-mixin-aws'
25
- s.add_runtime_dependency 'aws-sdk'
24
+ s.add_runtime_dependency 'logstash-mixin-aws', '>= 1.0.0'
26
25
 
27
26
  s.add_development_dependency 'logstash-devutils'
28
27
  end
29
-
@@ -2,17 +2,141 @@
2
2
  require "logstash/devutils/rspec/spec_helper"
3
3
  require 'logstash/outputs/sns'
4
4
  require 'logstash/event'
5
+ require "logstash/plugin_mixins/aws_config"
6
+
7
+ require "aws-sdk" # TODO: Why is this not automatically brought in by the aws_config plugin?
5
8
 
6
9
  describe LogStash::Outputs::Sns do
7
- describe '#format_message' do
8
- it 'should allow to output to sns with empty tags' do
9
- event = LogStash::Event.new({ "message" => "42 is the answer" })
10
- expect(LogStash::Outputs::Sns.format_message(event)).to match(/Tags:\s\n/m)
10
+ let(:arn) { "arn:aws:sns:us-east-1:999999999:logstash-test-sns-topic" }
11
+ let(:sns_subject) { "The Plain in Spain" }
12
+ let(:sns_message) { "That's where the rain falls, plainly." }
13
+ let(:mock_client) { double("Aws::SNS::Client") }
14
+ let(:instance) {
15
+ allow(Aws::SNS::Client).to receive(:new).and_return(mock_client)
16
+ inst = LogStash::Outputs::Sns.new
17
+ allow(inst).to receive(:publish_boot_message_arn).and_return(nil)
18
+ inst.register
19
+ inst
20
+ }
21
+
22
+ describe "receiving an event" do
23
+ let(:expected_subject) { double("expected_subject")}
24
+ subject {
25
+ inst = instance
26
+ allow(inst).to receive(:send_sns_message).with(any_args)
27
+ allow(inst).to receive(:event_subject).
28
+ with(any_args).
29
+ and_return(expected_subject)
30
+ inst.receive(event)
31
+ inst
32
+ }
33
+
34
+ shared_examples("publishing correctly") do
35
+ it "should send a message to the correct ARN if the event has 'arn' set" do
36
+ expect(subject).to have_received(:send_sns_message).with(arn, anything, anything)
37
+ end
38
+
39
+ it "should send the message" do
40
+ expect(subject).to have_received(:send_sns_message).with(anything, anything, expected_message)
41
+ end
42
+
43
+ it "should send the subject" do
44
+ expect(subject).to have_received(:send_sns_message).with(anything, expected_subject, anything)
45
+ end
46
+ end
47
+
48
+ describe "with an explicit message" do
49
+ let(:expected_subject) { sns_subject }
50
+ let(:expected_message) { sns_message }
51
+ let(:event) { LogStash::Event.new("sns" => arn, "sns_subject" => sns_subject,
52
+ "sns_message" => sns_message) }
53
+ include_examples("publishing correctly")
54
+ end
55
+
56
+ describe "without an explicit message" do
57
+ # Testing codecs sucks. It'd be nice if codecs had to implement some sort of encode_sync method
58
+ let(:expected_message) {
59
+ c = subject.codec.clone
60
+ result = nil;
61
+ c.on_event {|event, encoded| result = encoded }
62
+ c.encode(event)
63
+ result
64
+ }
65
+ let(:event) { LogStash::Event.new("sns" => arn, "sns_subject" => sns_subject) }
66
+
67
+ include_examples("publishing correctly")
68
+ end
69
+ end
70
+
71
+ describe "determining the subject" do
72
+ it "should return 'sns_subject' when set" do
73
+ event = LogStash::Event.new("sns_subject" => "foo")
74
+ expect(subject.send(:event_subject, event)).to eql("foo")
75
+ end
76
+
77
+ it "should return the sns subject as JSON if not a string" do
78
+ event = LogStash::Event.new("sns_subject" => ["foo", "bar"])
79
+ expect(subject.send(:event_subject, event)).to eql(LogStash::Json.dump(["foo", "bar"]))
11
80
  end
12
81
 
13
- it 'should allow to output to sns with a list of tags' do
14
- event = LogStash::Event.new({"tags" => ["elasticsearch", "logstash", "kibana"] })
15
- expect(LogStash::Outputs::Sns.format_message(event)).to match(/\nTags:\selasticsearch,\slogstash,\skibana\n/m)
82
+ it "should return the host if 'sns_subject' not set" do
83
+ event = LogStash::Event.new("host" => "foo")
84
+ expect(subject.send(:event_subject, event)).to eql("foo")
85
+ end
86
+
87
+ it "should return 'NO SUBJECT' when subject cannot be determined" do
88
+ event = LogStash::Event.new("foo" => "bar")
89
+ expect(subject.send(:event_subject, event)).to eql(LogStash::Outputs::Sns::NO_SUBJECT)
90
+ end
91
+ end
92
+
93
+ describe "sending an SNS notification" do
94
+ let(:good_publish_args) {
95
+ {
96
+ :topic_arn => arn,
97
+ :subject => sns_subject,
98
+ :message => sns_message
99
+ }
100
+ }
101
+ let(:long_message) { "A" * (LogStash::Outputs::Sns::MAX_MESSAGE_SIZE_IN_BYTES + 1) }
102
+ let(:long_subject) { "S" * (LogStash::Outputs::Sns::MAX_SUBJECT_SIZE_IN_CHARACTERS + 1) }
103
+ subject { instance }
104
+
105
+ it "should raise an ArgumentError if no arn is provided" do
106
+ expect {
107
+ subject.send(:send_sns_message, nil, sns_subject, sns_message)
108
+ }.to raise_error(ArgumentError)
109
+ end
110
+
111
+ it "should send a well formed message through to SNS" do
112
+ expect(mock_client).to receive(:publish).with(good_publish_args)
113
+ subject.send(:send_sns_message, arn, sns_subject, sns_message)
114
+ end
115
+
116
+ it "should attempt to publish a boot message" do
117
+ expect(subject).to have_received(:publish_boot_message_arn).once
118
+ x = case "foo"
119
+ when "bar"
120
+ "hello"
121
+ end
122
+ end
123
+
124
+ it "should truncate long messages before sending" do
125
+ max_size = LogStash::Outputs::Sns::MAX_MESSAGE_SIZE_IN_BYTES
126
+ expect(mock_client).to receive(:publish) {|args|
127
+ expect(args[:message].bytesize).to eql(max_size)
128
+ }
129
+
130
+ subject.send(:send_sns_message, arn, sns_subject, long_message)
131
+ end
132
+
133
+ it "should truncate long subjects before sending" do
134
+ max_size = LogStash::Outputs::Sns::MAX_SUBJECT_SIZE_IN_CHARACTERS
135
+ expect(mock_client).to receive(:publish) {|args|
136
+ expect(args[:subject].bytesize).to eql(max_size)
137
+ }
138
+
139
+ subject.send(:send_sns_message, arn, long_subject, sns_message)
16
140
  end
17
141
  end
18
142
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-output-sns
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-24 00:00:00.000000000 Z
11
+ date: 2015-06-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logstash-core
@@ -36,26 +36,12 @@ dependencies:
36
36
  requirements:
37
37
  - - '>='
38
38
  - !ruby/object:Gem::Version
39
- version: '0'
39
+ version: 1.0.0
40
40
  requirement: !ruby/object:Gem::Requirement
41
41
  requirements:
42
42
  - - '>='
43
43
  - !ruby/object:Gem::Version
44
- version: '0'
45
- prerelease: false
46
- type: :runtime
47
- - !ruby/object:Gem::Dependency
48
- name: aws-sdk
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - '>='
52
- - !ruby/object:Gem::Version
53
- version: '0'
54
- requirement: !ruby/object:Gem::Requirement
55
- requirements:
56
- - - '>='
57
- - !ruby/object:Gem::Version
58
- version: '0'
44
+ version: 1.0.0
59
45
  prerelease: false
60
46
  type: :runtime
61
47
  - !ruby/object:Gem::Dependency
@@ -111,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
97
  version: '0'
112
98
  requirements: []
113
99
  rubyforge_project:
114
- rubygems_version: 2.2.2
100
+ rubygems_version: 2.1.9
115
101
  signing_key:
116
102
  specification_version: 4
117
103
  summary: Send events to Amazon's Simple Notification Service a hosted pub/sub framework