logstash-output-sns 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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