logstash-codec-protobuf 1.0.5 → 1.2.5

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 (46) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +20 -1
  3. data/Gemfile +1 -1
  4. data/LICENSE +2 -3
  5. data/README.md +147 -40
  6. data/docs/index.asciidoc +173 -41
  7. data/lib/logstash/codecs/protobuf.rb +598 -238
  8. data/logstash-codec-protobuf.gemspec +3 -3
  9. data/spec/codecs/{protobuf_spec.rb → pb2_spec.rb} +81 -54
  10. data/spec/codecs/pb3_decode_spec.rb +445 -0
  11. data/spec/codecs/pb3_encode_spec.rb +243 -0
  12. data/spec/helpers/pb2/event.pb.rb +19 -0
  13. data/spec/helpers/pb2/event.proto +12 -0
  14. data/spec/helpers/pb2/header/header.pb.rb +16 -0
  15. data/spec/helpers/pb2/header/header.proto +8 -0
  16. data/spec/helpers/pb3/FantasyHorse_pb.rb +44 -0
  17. data/spec/helpers/pb3/ProbeResult_pb.rb +26 -0
  18. data/spec/helpers/pb3/dnsmessage_pb.rb +82 -0
  19. data/spec/helpers/pb3/events.proto3 +10 -0
  20. data/spec/helpers/pb3/events_pb.rb +17 -0
  21. data/spec/helpers/pb3/header/header.proto3 +7 -0
  22. data/spec/helpers/pb3/header/header_pb.rb +12 -0
  23. data/spec/helpers/pb3/integertest_pb.rb +20 -0
  24. data/spec/helpers/pb3/messageA.proto3 +12 -0
  25. data/spec/helpers/pb3/messageA_pb.rb +16 -0
  26. data/spec/helpers/pb3/messageB.proto3 +12 -0
  27. data/spec/helpers/pb3/messageB_pb.rb +16 -0
  28. data/spec/helpers/pb3/rum2_pb.rb +87 -0
  29. data/spec/helpers/pb3/rum3_pb.rb +87 -0
  30. data/spec/helpers/pb3/rum_pb.rb +87 -0
  31. metadata +62 -34
  32. data/lib/net/jpountz/lz4/lz4/1.3.0/lz4-1.3.0.jar +0 -0
  33. data/lib/org/apache/kafka/kafka-clients/0.11.0.0/kafka-clients-0.11.0.0.jar +0 -0
  34. data/lib/org/apache/logging/log4j/log4j-api/2.8.2/log4j-api-2.8.2.jar +0 -0
  35. data/lib/org/apache/logging/log4j/log4j-slf4j-impl/2.8.2/log4j-slf4j-impl-2.8.2.jar +0 -0
  36. data/lib/org/slf4j/slf4j-api/1.7.24/slf4j-api-1.7.24.jar +0 -0
  37. data/lib/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar +0 -0
  38. data/lib/org/xerial/snappy/snappy-java/1.1.2.6/snappy-java-1.1.2.6.jar +0 -0
  39. data/spec/codecs/protobuf3_spec.rb +0 -147
  40. data/vendor/jar-dependencies/runtime-jars/kafka-clients-0.11.0.0.jar +0 -0
  41. data/vendor/jar-dependencies/runtime-jars/log4j-api-2.8.2.jar +0 -0
  42. data/vendor/jar-dependencies/runtime-jars/log4j-slf4j-impl-2.8.2.jar +0 -0
  43. data/vendor/jar-dependencies/runtime-jars/lz4-1.3.0.jar +0 -0
  44. data/vendor/jar-dependencies/runtime-jars/slf4j-api-1.7.24.jar +0 -0
  45. data/vendor/jar-dependencies/runtime-jars/slf4j-api-1.7.25.jar +0 -0
  46. data/vendor/jar-dependencies/runtime-jars/snappy-java-1.1.2.6.jar +0 -0
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-codec-protobuf'
4
- s.version = '1.0.5'
4
+ s.version = '1.2.5'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "Reads protobuf messages and converts to Logstash Events"
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"
@@ -20,8 +20,8 @@ Gem::Specification.new do |s|
20
20
 
21
21
  # Gem dependencies
22
22
  s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
23
- s.add_runtime_dependency 'google-protobuf', '3.1'
23
+ s.add_runtime_dependency 'google-protobuf', '3.5.0.pre'
24
24
  s.add_runtime_dependency 'ruby-protocol-buffers' # for protobuf 2
25
25
  s.add_development_dependency 'logstash-devutils'
26
- end
27
26
 
27
+ end
@@ -2,30 +2,32 @@
2
2
  require "logstash/devutils/rspec/spec_helper"
3
3
  require "logstash/codecs/protobuf"
4
4
  require "logstash/event"
5
- require "insist"
6
-
7
5
 
8
6
  require 'protocol_buffers' # https://github.com/codekitchen/ruby-protocol-buffers, for protobuf2
9
7
 
8
+ require_relative '../helpers/pb2/header/header.pb.rb'
9
+ require_relative '../helpers/pb2/event.pb.rb'
10
+
10
11
 
11
12
  describe LogStash::Codecs::Protobuf do
12
13
 
14
+ pb_include_path = File.expand_path(".") + "/spec/helpers"
13
15
 
16
+ context "#test1" do
14
17
 
15
- context "#decodePB2" do
16
18
 
17
19
 
18
20
  #### Test case 1: Decode simple protobuf bytes for unicorn ####################################################################################################################
19
- let(:plugin_unicorn) { LogStash::Codecs::Protobuf.new("class_name" => "Animal::Unicorn", "include_path" => ['spec/helpers/pb2/unicorn.pb.rb']) }
21
+ let(:plugin_unicorn) { LogStash::Codecs::Protobuf.new("class_name" => "Animal::Unicorn", "include_path" => [pb_include_path + '/pb2/unicorn.pb.rb']) }
20
22
  before do
21
- plugin_unicorn.register
23
+ plugin_unicorn.register
22
24
  end
23
25
 
24
26
  it "should return an event from protobuf encoded data" do
25
-
27
+
26
28
  data = {:colour => 'rainbow', :horn_length => 18, :last_seen => 1420081471, :has_wings => true}
27
29
  unicorn = Animal::Unicorn.new(data)
28
-
30
+
29
31
  plugin_unicorn.decode(unicorn.serialize_to_string) do |event|
30
32
  expect(event.get("colour") ).to eq(data[:colour] )
31
33
  expect(event.get("horn_length") ).to eq(data[:horn_length] )
@@ -34,34 +36,34 @@ describe LogStash::Codecs::Protobuf do
34
36
  end
35
37
  end # it
36
38
 
37
-
39
+ end
38
40
 
39
41
  #### Test case 2: Decode complex protobuf bytes for human #####################################################################################################################
40
42
 
43
+ context "#test2" do
41
44
 
42
-
43
-
44
- let(:plugin_human) { LogStash::Codecs::Protobuf.new("class_name" => "Animal::Human", "include_path" => ['spec/helpers/pb2/human.pb.rb']) }
45
+
46
+ let(:plugin_human) { LogStash::Codecs::Protobuf.new("class_name" => "Animal::Human", "include_path" => [pb_include_path + '/pb2/human.pb.rb']) }
45
47
  before do
46
- plugin_human.register
48
+ plugin_human.register
47
49
  end
48
50
 
49
51
  it "should return an event from complex nested protobuf encoded data" do
50
-
52
+
51
53
  data_gm = {:first_name => 'Elisabeth', :last_name => "Oliveoil", :middle_names => ["Maria","Johanna"], :vegetarian=>true}
52
54
  grandmother = Animal::Human.new(data_gm)
53
55
  data_m = {:first_name => 'Annemarie', :last_name => "Smørebrød", :mother => grandmother}
54
56
  mother = Animal::Human.new(data_m)
55
57
  data_f = {:first_name => 'Karl', :middle_names => ["Theodor-Augustin"], :last_name => "Falkenstein"}
56
58
  father = Animal::Human.new(data_f)
57
- data = {:first_name => 'Hugo', :middle_names => ["Heinz", "Peter"], :last_name => "Smørebrød",:father => father, :mother => mother}
59
+ data = {:first_name => 'Hugo', :middle_names => ["Heinz", "Peter"], :last_name => "Smørebrød",:father => father, :mother => mother}
58
60
  hugo = Animal::Human.new(data)
59
-
61
+
60
62
  plugin_human.decode(hugo.serialize_to_string) do |event|
61
63
  expect(event.get("first_name") ).to eq(data[:first_name] )
62
64
  expect(event.get("middle_names") ).to eq(data[:middle_names] )
63
65
  expect(event.get("last_name") ).to eq(data[:last_name] )
64
- expect(event.get("[mother][first_name]") ).to eq(data_m[:first_name] )
66
+ expect(event.get("[mother][first_name]") ).to eq(data_m[:first_name] )
65
67
  expect(event.get("[father][first_name]") ).to eq(data_f[:first_name] )
66
68
  expect(event.get("[mother][last_name]") ).to eq(data_m[:last_name] )
67
69
  expect(event.get("[mother][mother][last_name]") ).to eq(data_gm[:last_name] )
@@ -72,28 +74,25 @@ describe LogStash::Codecs::Protobuf do
72
74
  expect(event.get("[father][middle_names]") ).to eq(data_f[:middle_names] )
73
75
  end
74
76
  end # it
75
-
76
-
77
-
78
-
77
+ end # context
79
78
 
80
79
 
81
80
  #### Test case 3: Decoder test for enums #####################################################################################################################
82
81
 
82
+ context "#test3" do
83
83
 
84
-
85
-
86
- let(:plugin_col) { LogStash::Codecs::Protobuf.new("class_name" => "ColourProtoTest", "include_path" => ['spec/helpers/pb2/ColourTestcase.pb.rb']) }
84
+
85
+ let(:plugin_col) { LogStash::Codecs::Protobuf.new("class_name" => "ColourProtoTest", "include_path" => [pb_include_path + '/pb2/ColourTestcase.pb.rb']) }
87
86
  before do
88
- plugin_col.register
87
+ plugin_col.register
89
88
  end
90
89
 
91
90
  it "should return an event from protobuf encoded data with enums" do
92
-
91
+
93
92
  data = {:least_liked => ColourProtoTest::Colour::YELLOW, :favourite_colours => \
94
- [ColourProtoTest::Colour::BLACK, ColourProtoTest::Colour::BLUE], :booleantest => [true, false, true]}
93
+ [ColourProtoTest::Colour::BLACK, ColourProtoTest::Colour::BLUE], :booleantest => [true, false, true]}
95
94
  pb = ColourProtoTest.new(data)
96
-
95
+
97
96
  plugin_col.decode(pb.serialize_to_string) do |event|
98
97
  expect(event.get("least_liked") ).to eq(data[:least_liked] )
99
98
  expect(event.get("favourite_colours") ).to eq(data[:favourite_colours] )
@@ -102,31 +101,28 @@ describe LogStash::Codecs::Protobuf do
102
101
  end # it
103
102
 
104
103
 
105
- end # context decodePB2
106
-
107
-
108
-
104
+ end # context test3
109
105
 
110
106
 
111
107
  #### Test case 4: Encode simple protobuf bytes for unicorn ####################################################################################################################
112
108
 
113
109
  context "#encodePB2-a" do
114
110
  subject do
115
- next LogStash::Codecs::Protobuf.new("class_name" => "Animal::UnicornEvent", "include_path" => ['spec/helpers/pb2/unicorn_event.pb.rb'])
111
+ next LogStash::Codecs::Protobuf.new("class_name" => "Animal::UnicornEvent", "include_path" => [pb_include_path + '/pb2/unicorn_event.pb.rb'])
116
112
  end
117
113
 
118
- event = LogStash::Event.new("colour" => "pink", "horn_length" => 12, "last_seen" => 1410081999, "has_wings" => true)
114
+ event = LogStash::Event.new("colour" => "pink", "horn_length" => 12, "last_seen" => 1410081999, "has_wings" => true)
119
115
 
120
116
  it "should return protobuf encoded data from a simple event" do
121
117
  subject.on_event do |event, data|
122
- insist { data.is_a? String }
123
- unicorn = Animal::UnicornEvent.parse(data)
124
-
118
+ expect(data).to be_a(String)
119
+ unicorn = Animal::UnicornEvent.parse(data)
120
+
125
121
  expect(unicorn.colour ).to eq(event.get("colour") )
126
122
  expect(unicorn.horn_length ).to eq(event.get("horn_length") )
127
123
  expect(unicorn.last_seen ).to eq(event.get("last_seen") )
128
124
  expect(unicorn.has_wings ).to eq(event.get("has_wings") )
129
-
125
+
130
126
  end # subject.on_event
131
127
  subject.encode(event)
132
128
  end # it
@@ -136,23 +132,23 @@ describe LogStash::Codecs::Protobuf do
136
132
 
137
133
 
138
134
  #### Test case 5: encode complex protobuf bytes for human #####################################################################################################################
139
-
140
-
135
+
136
+
141
137
  context "#encodePB2-b" do
142
138
  subject do
143
- next LogStash::Codecs::Protobuf.new("class_name" => "Animal::Human", "include_path" => ['spec/helpers/pb2/human.pb.rb'])
139
+ next LogStash::Codecs::Protobuf.new("class_name" => "Animal::Human", "include_path" => [pb_include_path + '/pb2/human.pb.rb'])
144
140
  end
145
141
 
146
142
  event = LogStash::Event.new("first_name" => "Jimmy", "middle_names" => ["Bob", "James"], "last_name" => "Doe" \
147
143
  , "mother" => {"first_name" => "Jane", "middle_names" => ["Elizabeth"], "last_name" => "Doe" , "age" => 83, "vegetarian"=> false} \
148
- , "father" => {"first_name" => "John", "last_name" => "Doe", "@email" => "character_replacement_test@nothing" })
144
+ , "father" => {"first_name" => "John", "last_name" => "Doe", "@email" => "character_replacement_test@nothing" })
149
145
 
150
146
  it "should return protobuf encoded data from a complex event" do
151
147
 
152
148
  subject.on_event do |event, data|
153
- insist { data.is_a? String }
154
- jimmy = Animal::Human.parse(data)
155
-
149
+ expect(data).to be_a(String)
150
+ jimmy = Animal::Human.parse(data)
151
+
156
152
  expect(jimmy.first_name ).to eq(event.get("first_name") )
157
153
  expect(jimmy.middle_names ).to eq(event.get("middle_names") )
158
154
  expect(jimmy.last_name ).to eq(event.get("last_name") )
@@ -164,7 +160,7 @@ describe LogStash::Codecs::Protobuf do
164
160
  expect(jimmy.father.last_name ).to eq(event.get("[father][last_name]") )
165
161
  expect(jimmy.father.email ).to eq(event.get("[father][@email]") ) # recursion test for keys
166
162
  expect(jimmy.mother.last_name ).to eq(event.get("[mother][last_name]") )
167
-
163
+
168
164
  end # subject.on_event
169
165
  subject.encode(event)
170
166
  end # it
@@ -175,35 +171,66 @@ describe LogStash::Codecs::Protobuf do
175
171
 
176
172
 
177
173
  #### Test case 6: encode enums #########################################################################################################################
178
-
179
174
 
180
-
175
+
176
+
181
177
  context "#encodePB2-c" do
182
178
  subject do
183
- next LogStash::Codecs::Protobuf.new("class_name" => "ColourProtoTest", "include_path" => ['spec/helpers/pb2/ColourTestcase.pb.rb'])
179
+ next LogStash::Codecs::Protobuf.new("class_name" => "ColourProtoTest", "include_path" => [pb_include_path + '/pb2/ColourTestcase.pb.rb'])
184
180
  end
185
181
 
186
- require 'spec/helpers/pb2/ColourTestcase.pb.rb' # otherwise we cant use the colour enums in the next line
182
+ require_relative '../helpers/pb2/ColourTestcase.pb.rb' # otherwise we cant use the colour enums in the next line
183
+ # ^ this import is run from the spec directory, $LOGSTASH_DIR/spec/codecs/
184
+
187
185
  event = LogStash::Event.new("booleantest" => [false, false, true], "least_liked" => ColourProtoTest::Colour::YELLOW, "favourite_colours" => \
188
- [ColourProtoTest::Colour::BLACK, ColourProtoTest::Colour::BLUE] )
186
+ [ColourProtoTest::Colour::BLACK, ColourProtoTest::Colour::BLUE] )
189
187
 
190
188
  it "should return protobuf encoded data from a complex event with enums" do
191
189
 
192
190
  subject.on_event do |event, data|
193
- insist { data.is_a? String }
191
+ expect(data).to be_a(String)
192
+
193
+ colpref = ColourProtoTest.parse(data)
194
194
 
195
- colpref = ColourProtoTest.parse(data)
196
-
197
195
  expect(colpref.booleantest ).to eq(event.get("booleantest") )
198
196
  expect(colpref.least_liked ).to eq(event.get("least_liked") )
199
197
  expect(colpref.favourite_colours ).to eq(event.get("favourite_colours") )
200
198
 
201
-
199
+
202
200
  end # subject.on_event
203
201
  subject.encode(event)
204
202
  end # it
205
203
  end # context
206
204
 
205
+ #### Test case 7: decode a message automatically loading the dependencies ######################################################################################
206
+
207
+ context "#test4" do
208
+ let(:plugin) { LogStash::Codecs::Protobuf.new(
209
+ "class_name" => "Logging::Event",
210
+ "class_file" => [ 'event.pb.rb' ],
211
+ "protobuf_root_directory" => pb_include_path + '/pb2/')
212
+ }
213
+
214
+ before do
215
+ plugin.register
216
+ end
217
+
218
+ it "should return an event from protobuf encoded data loading the dependencies" do
219
+ header = Grpc::Header.new(:protocol => 'https')
220
+
221
+ data = {
222
+ :name => "Test",
223
+ :header => header,
224
+ }
225
+
226
+ pb = Logging::Event.new(data)
227
+
228
+ plugin.decode(pb.serialize_to_string) do |event|
229
+ expect(event.get("name") ).to eq(data[:name])
230
+ expect(event.get("header") ).to eq({"protocol"=>"https"})
231
+ end
232
+ end # it
233
+ end # context test4
207
234
 
208
235
 
209
236
  end # describe
@@ -0,0 +1,445 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/codecs/protobuf"
4
+ require "logstash/event"
5
+
6
+ require 'google/protobuf' # for protobuf3
7
+
8
+ # absolute path to the protobuf helpers directory
9
+ pb_include_path = File.expand_path(".") + "/spec/helpers"
10
+
11
+ require pb_include_path + '/pb3/unicorn_pb.rb'
12
+ unicorn_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("Unicorn").msgclass
13
+
14
+ describe LogStash::Codecs::Protobuf do
15
+
16
+ context ".reloadable?" do
17
+ subject do
18
+ next LogStash::Codecs::Protobuf.new(
19
+ "class_name" => "Unicorn",
20
+ "include_path" => [pb_include_path + '/pb3/unicorn_pb.rb'],
21
+ "protobuf_version" => 3
22
+ )
23
+ end
24
+
25
+ it "returns false" do
26
+ expect(subject.reloadable?).to be_falsey
27
+ end
28
+ end
29
+
30
+ context "config" do
31
+ context "using class_file and include_path" do
32
+ let(:plugin) {
33
+ LogStash::Codecs::Protobuf.new(
34
+ "class_name" => "Unicorn",
35
+ "include_path" => [pb_include_path + '/pb3/unicorn_pb.rb'],
36
+ "class_file" => pb_include_path + '/pb3/unicorn_pb.rb',
37
+ "protobuf_version" => 3
38
+ )
39
+ }
40
+
41
+ it "should fail to register the plugin with ConfigurationError" do
42
+ expect {plugin.register}.to raise_error(LogStash::ConfigurationError, /`include_path` and `class_file`/)
43
+ end # it
44
+ end
45
+
46
+ context "not using class_file or include_path" do
47
+ let(:plugin) {
48
+ LogStash::Codecs::Protobuf.new("class_name" => "Unicorn")
49
+ }
50
+
51
+ it "should fail to register the plugin with ConfigurationError" do
52
+ expect {plugin.register}.to raise_error(LogStash::ConfigurationError, /`include_path` or `class_file`/)
53
+ end # it
54
+ end
55
+
56
+ RSpec::Expectations.configuration.on_potential_false_positives = :nothing
57
+
58
+ context "re-registering the plugin with a valid configuration" do
59
+ let(:plugin) { LogStash::Codecs::Protobuf.new(
60
+ "class_name" => "A.MessageA",
61
+ "class_file" => [ pb_include_path + '/pb3/messageA_pb.rb' ],
62
+ "protobuf_version" => 3,
63
+ "protobuf_root_directory" => File.expand_path(File.dirname(__FILE__) + pb_include_path + '/pb3/'))
64
+ }
65
+
66
+ it "should not fail" do
67
+ expect {
68
+ # this triggers the `register()` method of the plugin multiple times
69
+ plugin.register
70
+ plugin.register
71
+ }.not_to raise_error(RuntimeError)
72
+ end # it
73
+ end
74
+ end # context
75
+
76
+ context "#pb3decoder_test1" do
77
+
78
+
79
+ #### Test case 1: Decode simple protobuf ####################################################################################################################
80
+ let(:plugin_unicorn) { LogStash::Codecs::Protobuf.new(
81
+ "class_name" => "Unicorn", "include_path" => [pb_include_path + '/pb3/unicorn_pb.rb'], "protobuf_version" => 3)
82
+ }
83
+
84
+ it "should return an event from protobuf data" do
85
+
86
+ unicorn_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("Unicorn").msgclass
87
+ data = {:name => 'Pinkie', :age => 18, :is_pegasus => false, :favourite_numbers => [4711,23], :fur_colour => Colour::PINK,
88
+ :favourite_colours => [Colour::GREEN, Colour::BLUE]
89
+ }
90
+
91
+ unicorn_object = unicorn_class.new(data)
92
+ bin = unicorn_class.encode(unicorn_object)
93
+ plugin_unicorn.decode(bin) do |event|
94
+ expect(event.get("name") ).to eq(data[:name] )
95
+ expect(event.get("age") ).to eq(data[:age])
96
+ expect(event.get("fur_colour") ).to eq("PINK")
97
+ expect(event.get("favourite_numbers") ).to eq(data[:favourite_numbers])
98
+ expect(event.get("favourite_colours") ).to eq(["GREEN","BLUE"])
99
+ expect(event.get("is_pegasus") ).to eq(data[:is_pegasus] )
100
+ end
101
+ end # it
102
+ end # context
103
+
104
+ context "#pb3decoder_test2" do
105
+
106
+ #### Test case 2: decode nested protobuf ####################################################################################################################
107
+ let(:plugin_unicorn) { LogStash::Codecs::Protobuf.new("class_name" => "Unicorn", "include_path" => [pb_include_path + '/pb3/unicorn_pb.rb'], "protobuf_version" => 3) }
108
+
109
+ it "should return an event from protobuf data with nested classes" do
110
+ father = unicorn_class.new({:name=> "Sparkle", :age => 50, :fur_colour => 3 })
111
+ data = {:name => 'Glitter', :fur_colour => Colour::GLITTER, :father => father}
112
+
113
+ unicorn_object = unicorn_class.new(data)
114
+ bin = unicorn_class.encode(unicorn_object)
115
+ plugin_unicorn.decode(bin) do |event|
116
+ expect(event.get("name") ).to eq(data[:name] )
117
+ expect(event.get("fur_colour") ).to eq("GLITTER" )
118
+ expect(event.get("father")["name"] ).to eq(data[:father][:name] )
119
+ expect(event.get("father")["age"] ).to eq(data[:father][:age] )
120
+ expect(event.get("father")["fur_colour"] ).to eq("SILVER")
121
+
122
+ end
123
+ end # it
124
+
125
+ end # context
126
+
127
+ context "#pb3decoder_test3" do
128
+
129
+ #### Test case 3: decode ProbeResult ####################################################################################################################
130
+ let(:plugin_3) { LogStash::Codecs::Protobuf.new("class_name" => "ProbeResult", "include_path" => [pb_include_path + '/pb3/ProbeResult_pb.rb'], "protobuf_version" => 3) }
131
+
132
+ before do
133
+ plugin_3.register
134
+ end
135
+
136
+ it "should return an event from protobuf data with nested classes" do
137
+
138
+ probe_result_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("ProbeResult").msgclass
139
+ ping_result_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("PingIPv4Result").msgclass
140
+
141
+ ping_result_data = {:status=> PingIPv4Result::Status::ERROR,
142
+ :latency => 50, :ip => "8.8.8.8", :probe_ip => "127.0.0.1", :geolocation => "New York City" }
143
+ ping_result_object = ping_result_class.new(ping_result_data)
144
+
145
+ probe_result_data = {:UUID => '12345678901233456789', :TaskPingIPv4Result => ping_result_object}
146
+ probe_result_object = probe_result_class.new(probe_result_data)
147
+ bin = probe_result_class.encode(probe_result_object)
148
+ plugin_3.decode(bin) do |event|
149
+ expect(event.get("UUID") ).to eq(probe_result_data[:UUID] )
150
+ expect(event.get("TaskPingIPv4Result")["status"] ).to eq("ERROR")
151
+ expect(event.get("TaskPingIPv4Result")["latency"] ).to eq(ping_result_data[:latency] )
152
+ expect(event.get("TaskPingIPv4Result")["ip"] ).to eq(ping_result_data[:ip] )
153
+ expect(event.get("TaskPingIPv4Result")["probe_ip"] ).to eq(ping_result_data[:probe_ip] )
154
+ expect(event.get("TaskPingIPv4Result")["geolocation"] ).to eq(ping_result_data[:geolocation] )
155
+ end
156
+ end # it
157
+ end # context
158
+
159
+ context "#pb3decoder_test4" do
160
+
161
+ #### Test case 4: decode PBDNSMessage ####################################################################################################################
162
+ let(:plugin_4) { LogStash::Codecs::Protobuf.new("class_name" => "PBDNSMessage", "include_path" => [pb_include_path + '/pb3/dnsmessage_pb.rb'], "protobuf_version" => 3) }
163
+
164
+ before do
165
+ plugin_4.register
166
+ end
167
+
168
+ it "should return an event from protobuf data with nested classes" do
169
+
170
+
171
+ pbdns_message_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("PBDNSMessage").msgclass
172
+ dns_question_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("PBDNSMessage.DNSQuestion").msgclass
173
+ dns_response_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("PBDNSMessage.DNSResponse").msgclass
174
+ dns_rr_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("PBDNSMessage.DNSResponse.DNSRR").msgclass
175
+
176
+ dns_question_data = {:qName => "Foo", :qType => 12345, :qClass => 67890 }
177
+ dns_question_object = dns_question_class.new(dns_question_data)
178
+
179
+ dns_response_data = {:rcode => 12345, :appliedPolicy => "baz", :tags => ["a","b","c"],
180
+ :queryTimeSec => 123, :queryTimeUsec => 456,
181
+ :appliedPolicyType => PBDNSMessage::PolicyType::NSIP}
182
+
183
+ dns_rr_data = [
184
+ {:name => "abc", :type => 9000, :class => 8000, :ttl => 20, :rdata => "300"},
185
+ {:name => "def", :type => 19000, :class => 18000, :ttl => 120, :rdata => "1300"}
186
+ ]
187
+
188
+ dns_response_data[:rrs] = dns_rr_data.map { | d | d = dns_rr_class.new(d) }
189
+ dns_response_object = dns_response_class.new(dns_response_data)
190
+
191
+ pbdns_message_data = {
192
+ # :UUID => '12345678901233456789', :TaskPingIPv4Result => ping_result_object
193
+ :type => PBDNSMessage::Type::DNSIncomingResponseType,
194
+ :messageId => "15",
195
+ :serverIdentity => "16",
196
+ :socketFamily => PBDNSMessage::SocketFamily::INET6,
197
+ :socketProtocol => PBDNSMessage::SocketProtocol::TCP,
198
+ :from => "17",
199
+ :to => "18",
200
+ :inBytes => 70000,
201
+ :timeSec => 80000,
202
+ :timeUsec => 90000,
203
+ :id => 20000,
204
+ :question => dns_question_object,
205
+ :response => dns_response_object,
206
+ :originalRequestorSubnet => "19",
207
+ :requestorId => "Bar",
208
+ :initialRequestId => "20",
209
+ :deviceId => "21",
210
+ }
211
+ pbdns_message_object = pbdns_message_class.new(pbdns_message_data)
212
+ bin = pbdns_message_class.encode(pbdns_message_object)
213
+ plugin_4.decode(bin) do |event|
214
+
215
+ ['messageId', 'serverIdentity','from','to','inBytes','timeUsec','timeSec','id', 'originalRequestorSubnet', 'requestorId' ,'initialRequestId','deviceIdf'].each { |n|
216
+ expect(event.get(n)).to eq(pbdns_message_data[n.to_sym] ) }
217
+
218
+ # enum test:
219
+ expect(event.get("type") ).to eq("DNSIncomingResponseType" )
220
+ expect(event.get("socketFamily") ).to eq("INET6" )
221
+ expect(event.get("socketProtocol") ).to eq("TCP" )
222
+
223
+ expect(event.get("question")["qName"] ).to eq(dns_question_data[:qName] )
224
+ expect(event.get("question")["qType"] ).to eq(dns_question_data[:qType] )
225
+ expect(event.get("question")["qClass"] ).to eq(dns_question_data[:qClass] )
226
+
227
+ ['rcode', 'appliedPolicy','tags','queryTimeSec','queryTimeUsec'].each { |n| expect(event.get('response')[n]).to eq(dns_response_data[n.to_sym] ) }
228
+ expect(event.get("response")['appliedPolicyType'] ).to eq("NSIP" )
229
+
230
+ dns_rr_data.each_with_index { | data, index |
231
+ found = event.get("response")['rrs'][index]
232
+ ['name', 'type','class','ttl','rdata'].each { |n| expect(found[n]).to eq(data[n.to_sym]) }
233
+ }
234
+
235
+ end
236
+ end # it
237
+
238
+ end # context
239
+
240
+ context "#pb3decoder_test5" do
241
+
242
+ #### Test case 5: decode test case for github issue 17 ####################################################################################################################
243
+ let(:plugin_5) { LogStash::Codecs::Protobuf.new("class_name" => "com.foo.bar.IntegerTestMessage", "include_path" => [pb_include_path + '/pb3/integertest_pb.rb'], "protobuf_version" => 3) }
244
+
245
+ before do
246
+ plugin_5.register
247
+ end
248
+
249
+ it "should return an event from protobuf data with nested classes" do
250
+ integertest_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("com.foo.bar.IntegerTestMessage").msgclass
251
+ integertest_object = integertest_class.new({:response_time => 500})
252
+ bin = integertest_class.encode(integertest_object)
253
+ plugin_5.decode(bin) do |event|
254
+ expect(event.get("response_time") ).to eq(500)
255
+ end
256
+ end # it
257
+
258
+
259
+ end # context
260
+
261
+ context "#pb3decoder_test6" do
262
+
263
+
264
+ let(:execution_context) { double("execution_context")}
265
+ let(:pipeline_id) {rand(36**8).to_s(36)}
266
+
267
+ # Test case 6: decode a message automatically loading the dependencies ##############################################################################
268
+ let(:plugin) { LogStash::Codecs::Protobuf.new(
269
+ "class_name" => "A.MessageA",
270
+ "class_file" => [ 'messageA_pb.rb' ],
271
+ "protobuf_version" => 3,
272
+ "protobuf_root_directory" => pb_include_path + '/pb3/')
273
+ }
274
+
275
+ before do
276
+ allow(plugin).to receive(:execution_context).and_return(execution_context)
277
+ allow(execution_context).to receive(:pipeline_id).and_return(pipeline_id)
278
+
279
+ # this is normally done on the input plugins we "mock" it here to avoid
280
+ # instantiating a dummy input plugin. See
281
+ # https://github.com/ph/logstash/blob/37551a89b8137c1dc6fa4fbd992584c363a36065/logstash-core/lib/logstash/inputs/base.rb#L108
282
+ plugin.execution_context = execution_context
283
+ end
284
+
285
+ it "should return an event from protobuf data" do
286
+
287
+ header_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("Header").msgclass
288
+ header_data = {:name => {'a' => 'b'}}
289
+ header_object = header_class.new(header_data)
290
+
291
+ message_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.MessageA").msgclass
292
+ data = {:name => "Test name", :header => header_object}
293
+
294
+ message_object = message_class.new(data)
295
+ bin = message_class.encode(message_object)
296
+
297
+ plugin.decode(bin) do |event|
298
+ expect(event.get("name") ).to eq(data[:name] )
299
+ expect(event.get("header")['name'] ).to eq(header_data[:name])
300
+ end
301
+ end # it
302
+ end # context
303
+
304
+
305
+
306
+
307
+
308
+
309
+ context "#pb3decoder_test7" do
310
+
311
+ #### Test case 6: decode test case for github issue 17 ####################################################################################################################
312
+ let(:plugin_7) { LogStash::Codecs::Protobuf.new("class_name" => "RepeatedEvents", "include_path" => [pb_include_path + '/pb3/events_pb.rb'], "protobuf_version" => 3) }
313
+ before do
314
+ plugin_7.register
315
+ end
316
+
317
+ it "should return an event from protobuf data with repeated top level objects" do
318
+ event_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("RepeatedEvent").msgclass # TODO this shouldnt be necessary because the classes are already
319
+ # specified at the end of the _pb.rb files
320
+ events_class = Google::Protobuf::DescriptorPool.generated_pool.lookup("RepeatedEvents").msgclass
321
+ test_a = event_class.new({:id => "1", :msg => "a"})
322
+ test_b = event_class.new({:id => "2", :msg => "b"})
323
+ test_c = event_class.new({:id => "3", :msg => "c"})
324
+ event_obj = events_class.new({:repeated_events=>[test_a, test_b, test_c]})
325
+ bin = events_class.encode(event_obj)
326
+ plugin_7.decode(bin) do |event|
327
+ expect(event.get("repeated_events").size ).to eq(3)
328
+ expect(event.get("repeated_events")[0]["id"] ).to eq("1")
329
+ expect(event.get("repeated_events")[2]["id"] ).to eq("3")
330
+ expect(event.get("repeated_events")[0]["msg"] ).to eq("a")
331
+ expect(event.get("repeated_events")[2]["msg"] ).to eq("c")
332
+ end
333
+ end # it
334
+
335
+
336
+ end # context pb3decoder_test7
337
+
338
+
339
+ context "#pb3decoder_test8a" do
340
+
341
+ ########################################################################################################################
342
+ let(:plugin_8a) { LogStash::Codecs::Protobuf.new("class_name" => "FantasyHorse", "class_file" => 'pb3/FantasyHorse_pb.rb',
343
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => true) }
344
+ before do
345
+ plugin_8a.register
346
+ end
347
+
348
+ it "should add meta information on oneof fields" do
349
+ pegasus_data = {:wings_length => 100}
350
+ horsey = FantasyPegasus.new(pegasus_data)
351
+
352
+ braid_data = {:braid_thickness => 10, :braiding_style => "french"}
353
+ tail_data = {:tail_length => 80, :braided => BraidedHorseTail.new(braid_data) }
354
+ tail = FantasyHorseTail.new(tail_data)
355
+
356
+ data = {:name=>"Reinhold", :pegasus => horsey, :tail => tail}
357
+ pb_obj = FantasyHorse.new(data)
358
+ bin = FantasyHorse.encode(pb_obj)
359
+ plugin_8a.decode(bin) do |event|
360
+
361
+ expect(event.get("name") ).to eq(data[:name])
362
+ expect(event.get("pegasus")["wings_length"] ).to eq(pegasus_data[:wings_length])
363
+ expect(event.get("tail")['tail_length'] ).to eq(tail_data[:tail_length])
364
+ expect(event.get("tail")['braided']['braiding_style'] ).to eq(braid_data[:braiding_style])
365
+ expect(event.get("@metadata")["pb_oneof"]["horse_type"] ).to eq("pegasus")
366
+ expect(event.get("@metadata")["pb_oneof"]["tail"]["hair_type"] ).to eq("braided")
367
+
368
+ end
369
+ end # it
370
+
371
+
372
+ end # context pb3decoder_test8a
373
+
374
+
375
+
376
+
377
+ context "#pb3decoder_test8b" do
378
+
379
+ ########################################################################################################################
380
+ let(:plugin_8b) { LogStash::Codecs::Protobuf.new("class_name" => "FantasyHorse", "class_file" => 'pb3/FantasyHorse_pb.rb',
381
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => false) }
382
+ before do
383
+ plugin_8b.register
384
+ end
385
+
386
+ it "should not add meta information on oneof fields" do
387
+ pegasus_data = {:wings_length => 100}
388
+ horsey = FantasyPegasus.new(pegasus_data)
389
+
390
+ braid_data = {:braid_thickness => 10, :braiding_style => "french"}
391
+ tail_data = {:tail_length => 80, :braided => BraidedHorseTail.new(braid_data) }
392
+ tail = FantasyHorseTail.new(tail_data)
393
+
394
+ data = {:name=>"Winfried", :pegasus => horsey, :tail => tail}
395
+ pb_obj = FantasyHorse.new(data)
396
+ bin = FantasyHorse.encode(pb_obj)
397
+ plugin_8b.decode(bin) do |event|
398
+ expect(event.get("name") ).to eq(data[:name])
399
+ expect(event.get("pegasus")["wings_length"] ).to eq(pegasus_data[:wings_length])
400
+ expect(event.get("tail")['tail_length'] ).to eq(tail_data[:tail_length])
401
+ expect(event.get("tail")['braided']['braiding_style'] ).to eq(braid_data[:braiding_style])
402
+ expect(event.get("@metadata")["pb_oneof"]).to be_nil
403
+
404
+ end
405
+ end # it
406
+
407
+
408
+ end # context pb3decoder_test8b
409
+
410
+
411
+ context "#pb3decoder_test8c" do # same test as 8a just with different one_of options selected
412
+
413
+ ########################################################################################################################
414
+ let(:plugin_8c) { LogStash::Codecs::Protobuf.new("class_name" => "FantasyHorse", "class_file" => 'pb3/FantasyHorse_pb.rb',
415
+ "protobuf_root_directory" => pb_include_path, "protobuf_version" => 3, "pb3_set_oneof_metainfo" => true) }
416
+ before do
417
+ plugin_8c.register
418
+ end
419
+
420
+ it "should add meta information on oneof fields" do
421
+ unicorn_data = {:horn_length => 30}
422
+ horsey = FantasyUnicorn.new(unicorn_data)
423
+
424
+ natural_data = {:wavyness => "B"}
425
+ tail_data = {:tail_length => 80, :natural => NaturalHorseTail.new(natural_data) }
426
+ tail = FantasyHorseTail.new(tail_data)
427
+
428
+ data = {:name=>"Hubert", :unicorn => horsey, :tail => tail}
429
+ pb_obj = FantasyHorse.new(data)
430
+ bin = FantasyHorse.encode(pb_obj)
431
+ plugin_8c.decode(bin) do |event|
432
+ expect(event.get("name") ).to eq(data[:name])
433
+ expect(event.get("unicorn")["horn_length"] ).to eq(unicorn_data[:horn_length])
434
+ expect(event.get("tail")['tail_length'] ).to eq(tail_data[:tail_length])
435
+ expect(event.get("tail")['natural']['wavyness'] ).to eq(natural_data[:wavyness])
436
+ expect(event.get("@metadata")["pb_oneof"]["horse_type"] ).to eq("unicorn")
437
+ expect(event.get("@metadata")["pb_oneof"]["tail"]["hair_type"] ).to eq("natural")
438
+
439
+ end
440
+ end # it
441
+
442
+
443
+ end # context pb3decoder_test8c
444
+
445
+ end # describe