logstash-core 7.0.0.alpha2-java → 7.0.0.beta1-java

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/logstash/agent.rb +62 -57
  3. data/lib/logstash/compiler/lscl.rb +2 -3
  4. data/lib/logstash/config/config_ast.rb +59 -17
  5. data/lib/logstash/environment.rb +1 -1
  6. data/lib/logstash/instrument/metric_store.rb +1 -1
  7. data/lib/logstash/instrument/periodic_poller/dlq.rb +5 -7
  8. data/lib/logstash/instrument/periodic_poller/pq.rb +6 -8
  9. data/lib/logstash/instrument/periodic_pollers.rb +3 -3
  10. data/lib/logstash/java_pipeline.rb +36 -15
  11. data/lib/logstash/patches/resolv.rb +0 -21
  12. data/lib/logstash/pipeline.rb +27 -10
  13. data/lib/logstash/pipeline_action/base.rb +1 -1
  14. data/lib/logstash/pipeline_action/create.rb +7 -13
  15. data/lib/logstash/pipeline_action/reload.rb +35 -12
  16. data/lib/logstash/pipeline_action/stop.rb +4 -6
  17. data/lib/logstash/pipeline_settings.rb +1 -1
  18. data/lib/logstash/pipelines_registry.rb +166 -0
  19. data/lib/logstash/settings.rb +5 -5
  20. data/lib/logstash/state_resolver.rb +5 -5
  21. data/lib/logstash/util/duration_formatter.rb +1 -1
  22. data/lib/logstash/util/safe_uri.rb +1 -0
  23. data/lib/logstash/util.rb +11 -1
  24. data/locales/en.yml +1 -1
  25. data/logstash-core.gemspec +17 -20
  26. data/spec/logstash/acked_queue_concurrent_stress_spec.rb +1 -1
  27. data/spec/logstash/agent/converge_spec.rb +25 -31
  28. data/spec/logstash/agent_spec.rb +5 -5
  29. data/spec/logstash/event_spec.rb +2 -2
  30. data/spec/logstash/instrument/wrapped_write_client_spec.rb +1 -1
  31. data/spec/logstash/legacy_ruby_event_spec.rb +6 -5
  32. data/spec/logstash/pipeline_action/create_spec.rb +9 -8
  33. data/spec/logstash/pipeline_action/reload_spec.rb +10 -9
  34. data/spec/logstash/pipeline_action/stop_spec.rb +4 -3
  35. data/spec/logstash/pipelines_registry_spec.rb +220 -0
  36. data/spec/logstash/queue_factory_spec.rb +2 -1
  37. data/spec/logstash/runner_spec.rb +2 -0
  38. data/spec/logstash/settings/array_coercible_spec.rb +1 -1
  39. data/spec/logstash/settings/bytes_spec.rb +2 -2
  40. data/spec/logstash/settings/port_range_spec.rb +1 -1
  41. data/spec/logstash/state_resolver_spec.rb +26 -22
  42. data/spec/logstash/util/safe_uri_spec.rb +40 -0
  43. data/spec/logstash/util/time_value_spec.rb +1 -1
  44. data/spec/logstash/util/wrapped_acked_queue_spec.rb +1 -1
  45. data/spec/support/matchers.rb +25 -19
  46. data/spec/support/shared_contexts.rb +3 -3
  47. data/versions-gem-copy.yml +6 -6
  48. metadata +73 -88
@@ -49,7 +49,7 @@ describe LogStash::Agent do
49
49
 
50
50
  context "system pipeline" do
51
51
 
52
- let(:system_pipeline_config) { mock_pipeline_config(:system_pipeline, "input { generator { } } output { null {} }", { "pipeline.system" => true }) }
52
+ let(:system_pipeline_config) { mock_pipeline_config(:system_pipeline, "input { dummyblockinginput { } } output { null {} }", { "pipeline.system" => true }) }
53
53
 
54
54
  context "when we have a finite pipeline and a system pipeline running" do
55
55
 
@@ -65,40 +65,40 @@ describe LogStash::Agent do
65
65
  end
66
66
 
67
67
  context "when we have an infinite pipeline and a system pipeline running" do
68
- let(:infinite_pipeline_config) { mock_pipeline_config(:main, "input { generator { } } output { null {} }") }
68
+ let(:infinite_pipeline_config) { mock_pipeline_config(:main, "input { dummyblockinginput { } } output { null {} }") }
69
69
 
70
70
  let(:source_loader) do
71
71
  TestSourceLoader.new(infinite_pipeline_config, system_pipeline_config)
72
72
  end
73
73
 
74
74
  before(:each) do
75
- @agent_task = start_agent(subject)
75
+ @agent_task = start_agent(subject)
76
76
  end
77
77
 
78
78
  after(:each) do
79
- @agent_task.stop!
79
+ @agent_task.stop!
80
+ @agent_task.wait
81
+ subject.shutdown
80
82
  end
81
83
 
82
84
  describe "#running_user_defined_pipelines" do
83
85
  it "returns the user defined pipelines" do
84
- wait_for do
85
- subject.with_running_user_defined_pipelines {|pipelines| pipelines.keys }
86
- end.to eq([:main])
87
- end
86
+ # wait is necessary to accommodate for pipelines startup time
87
+ wait(60).for {subject.running_user_defined_pipelines.keys}.to eq([:main])
88
+ end
88
89
  end
89
90
 
90
91
  describe "#running_user_defined_pipelines?" do
91
92
  it "returns true" do
92
- wait_for do
93
- subject.running_user_defined_pipelines?
94
- end.to be_truthy
93
+ # wait is necessary to accommodate for pipelines startup time
94
+ wait(60).for {subject.running_user_defined_pipelines?}.to be_truthy
95
95
  end
96
96
  end
97
97
  end
98
98
  end
99
99
 
100
100
  context "when `config.reload.automatic`" do
101
- let(:pipeline_config) { mock_pipeline_config(:main, "input { generator {} } output { null {} }") }
101
+ let(:pipeline_config) { mock_pipeline_config(:main, "input { dummyblockinginput {} } output { null {} }") }
102
102
 
103
103
  let(:source_loader) do
104
104
  TestSourceLoader.new(pipeline_config)
@@ -114,14 +114,14 @@ describe LogStash::Agent do
114
114
 
115
115
  after(:each) do
116
116
  @agent_task.stop!
117
+ @agent_task.wait
118
+ subject.shutdown
117
119
  end
118
120
 
119
121
  it "converge only once" do
120
122
  wait(60).for { source_loader.fetch_count }.to eq(1)
121
-
123
+ # no need to wait here because have_running_pipeline? does the wait
122
124
  expect(subject).to have_running_pipeline?(pipeline_config)
123
-
124
- subject.shutdown
125
125
  end
126
126
  end
127
127
 
@@ -135,8 +135,6 @@ describe LogStash::Agent do
135
135
 
136
136
  expect(source_loader.fetch_count).to eq(1)
137
137
  expect(subject.pipelines_count).to eq(0)
138
-
139
- subject.shutdown
140
138
  end
141
139
  end
142
140
  end
@@ -149,26 +147,25 @@ describe LogStash::Agent do
149
147
  "config.reload.interval" => interval
150
148
  )
151
149
  end
150
+
152
151
  before(:each) do
153
152
  @agent_task = start_agent(subject)
154
153
  end
155
154
 
156
155
  after(:each) do
157
156
  @agent_task.stop!
157
+ @agent_task.wait
158
+ subject.shutdown
158
159
  end
159
160
 
160
161
  context "and successfully load the config" do
161
162
  it "converges periodically the pipelines from the configs source" do
162
- sleep(2) # let the interval reload a few times
163
+ # no need to wait here because have_running_pipeline? does the wait
163
164
  expect(subject).to have_running_pipeline?(pipeline_config)
164
165
 
165
166
  # we rely on a periodic thread to call fetch count, we have seen unreliable run on
166
167
  # travis, so lets add a few retries
167
- try do
168
- expect(source_loader.fetch_count).to be > 1
169
- end
170
-
171
- subject.shutdown
168
+ try { expect(source_loader.fetch_count).to be > 1 }
172
169
  end
173
170
  end
174
171
 
@@ -178,12 +175,9 @@ describe LogStash::Agent do
178
175
  end
179
176
 
180
177
  it "it will keep trying to converge" do
181
-
182
178
  sleep(agent_settings.get("config.reload.interval") / 1_000_000_000.0 * 20) # let the interval reload a few times
183
179
  expect(subject.pipelines_count).to eq(0)
184
180
  expect(source_loader.fetch_count).to be > 1
185
-
186
- subject.shutdown
187
181
  end
188
182
  end
189
183
  end
@@ -191,8 +185,8 @@ describe LogStash::Agent do
191
185
  end
192
186
 
193
187
  context "when shutting down the agent" do
194
- let(:pipeline_config) { mock_pipeline_config(:main, "input { generator {} } output { null {} }") }
195
- let(:new_pipeline_config) { mock_pipeline_config(:new, "input { generator { id => 'new' } } output { null {} }") }
188
+ let(:pipeline_config) { mock_pipeline_config(:main, "input { dummyblockinginput {} } output { null {} }") }
189
+ let(:new_pipeline_config) { mock_pipeline_config(:new, "input { dummyblockinginput { id => 'new' } } output { null {} }") }
196
190
 
197
191
  let(:source_loader) do
198
192
  TestSourceLoader.new([pipeline_config, new_pipeline_config])
@@ -205,8 +199,8 @@ describe LogStash::Agent do
205
199
  end
206
200
 
207
201
  context "Configuration converge scenario" do
208
- let(:pipeline_config) { mock_pipeline_config(:main, "input { generator {} } output { null {} }", { "pipeline.reloadable" => true }) }
209
- let(:new_pipeline_config) { mock_pipeline_config(:new, "input { generator {} } output { null {} }", { "pipeline.reloadable" => true }) }
202
+ let(:pipeline_config) { mock_pipeline_config(:main, "input { dummyblockinginput {} } output { null {} }", { "pipeline.reloadable" => true }) }
203
+ let(:new_pipeline_config) { mock_pipeline_config(:new, "input { dummyblockinginput {} } output { null {} }", { "pipeline.reloadable" => true }) }
210
204
 
211
205
  before do
212
206
  # Set the Agent to an initial state of pipelines
@@ -263,7 +257,7 @@ describe LogStash::Agent do
263
257
  end
264
258
 
265
259
  context "when the source return a modified pipeline" do
266
- let(:modified_pipeline_config) { mock_pipeline_config(:main, "input { generator { id => 'new-and-modified' } } output { null {} }", { "pipeline.reloadable" => true }) }
260
+ let(:modified_pipeline_config) { mock_pipeline_config(:main, "input { dummyblockinginput { id => 'new-and-modified' } } output { null {} }", { "pipeline.reloadable" => true }) }
267
261
 
268
262
  let(:source_loader) do
269
263
  TestSequenceSourceLoader.new(
@@ -119,7 +119,7 @@ describe LogStash::Agent do
119
119
  context "if state is clean" do
120
120
  before :each do
121
121
  allow(subject).to receive(:running_user_defined_pipelines?).and_return(true)
122
- allow(subject).to receive(:clean_state?).and_return(false)
122
+ allow(subject).to receive(:no_pipeline?).and_return(false)
123
123
  end
124
124
 
125
125
  it "should not converge state more than once" do
@@ -142,7 +142,7 @@ describe LogStash::Agent do
142
142
  it "does not upgrade the new config" do
143
143
  t = Thread.new { subject.execute }
144
144
  wait(timeout)
145
- .for { subject.running_pipelines? && subject.pipelines.values.first.ready? }
145
+ .for { subject.running_pipelines? && subject.running_pipelines.values.first.ready? }
146
146
  .to eq(true)
147
147
  expect(subject.converge_state_and_update).not_to be_a_successful_converge
148
148
  expect(subject).to have_running_pipeline?(mock_config_pipeline)
@@ -162,7 +162,7 @@ describe LogStash::Agent do
162
162
  it "does upgrade the new config" do
163
163
  t = Thread.new { subject.execute }
164
164
  Timeout.timeout(timeout) do
165
- sleep(0.1) until subject.pipelines_count > 0 && subject.pipelines.values.first.ready?
165
+ sleep(0.1) until subject.running_pipelines_count > 0 && subject.running_pipelines.values.first.ready?
166
166
  end
167
167
 
168
168
  expect(subject.converge_state_and_update).to be_a_successful_converge
@@ -186,7 +186,7 @@ describe LogStash::Agent do
186
186
  it "does not try to reload the pipeline" do
187
187
  t = Thread.new { subject.execute }
188
188
  Timeout.timeout(timeout) do
189
- sleep(0.1) until subject.running_pipelines? && subject.pipelines.values.first.running?
189
+ sleep(0.1) until subject.running_pipelines? && subject.running_pipelines.values.first.running?
190
190
  end
191
191
  expect(subject.converge_state_and_update).not_to be_a_successful_converge
192
192
  expect(subject).to have_running_pipeline?(mock_config_pipeline)
@@ -206,7 +206,7 @@ describe LogStash::Agent do
206
206
  it "tries to reload the pipeline" do
207
207
  t = Thread.new { subject.execute }
208
208
  Timeout.timeout(timeout) do
209
- sleep(0.1) until subject.running_pipelines? && subject.pipelines.values.first.running?
209
+ sleep(0.1) until subject.running_pipelines? && subject.running_pipelines.values.first.running?
210
210
  end
211
211
 
212
212
  expect(subject.converge_state_and_update).to be_a_successful_converge
@@ -141,10 +141,10 @@ describe LogStash::Event do
141
141
  expect(e.get("foo")).to eq(BigDecimal.new(1))
142
142
  end
143
143
 
144
- it "should set RubyBignum" do
144
+ it "should set RubyInteger" do
145
145
  e = LogStash::Event.new()
146
146
  e.set("[foo]", -9223372036854776000)
147
- expect(e.get("foo")).to be_kind_of(Bignum)
147
+ expect(e.get("foo")).to be_kind_of(Integer)
148
148
  expect(e.get("foo")).to eq(-9223372036854776000)
149
149
  end
150
150
 
@@ -110,7 +110,7 @@ describe LogStash::WrappedWriteClient do
110
110
 
111
111
  context "WrappedAckedQueue" do
112
112
  let(:path) { Stud::Temporary.directory }
113
- let(:queue) { LogStash::WrappedAckedQueue.new(path, 1024, 10, 1024, 1024, 1024, 4096) }
113
+ let(:queue) { LogStash::WrappedAckedQueue.new(path, 1024, 10, 1024, 1024, 1024, false, 4096) }
114
114
 
115
115
  before do
116
116
  read_client.set_events_metric(metric.namespace([:stats, :events]))
@@ -82,7 +82,7 @@ describe LogStash::Event do
82
82
  expect(event.get("reference_test")).not_to eq(data)
83
83
  end
84
84
 
85
- it "should not return a Fixnum reference" do
85
+ it "should not return an Integer reference" do
86
86
  data = 1
87
87
  event = LogStash::Event.new({ "reference" => data })
88
88
  LogStash::Util::Decorators.add_fields({"reference_test" => "%{reference}"}, event, "dummy-plugin")
@@ -98,11 +98,12 @@ describe LogStash::Event do
98
98
  expect(subject.sprintf("bonjour")).to eq("bonjour")
99
99
  end
100
100
 
101
- it "should raise error when formatting %{+%s} when @timestamp field is missing" do
101
+ it "should not raise error and should format as empty string when @timestamp field is missing" do
102
102
  str = "hello-%{+%s}"
103
103
  subj = subject.clone
104
104
  subj.remove("[@timestamp]")
105
- expect{ subj.sprintf(str) }.to raise_error(LogStash::Error)
105
+ expect{ subj.sprintf(str) }.not_to raise_error(LogStash::Error)
106
+ expect(subj.sprintf(str)).to eq("hello-")
106
107
  end
107
108
 
108
109
  it "should report a time with %{+format} syntax", :if => RUBY_ENGINE == "jruby" do
@@ -115,11 +116,11 @@ describe LogStash::Event do
115
116
  expect(subject.sprintf("foo %{+YYYY-MM-dd} %{type}")).to eq("foo 2013-01-01 sprintf")
116
117
  end
117
118
 
118
- it "should raise error with %{+format} syntax when @timestamp field is missing", :if => RUBY_ENGINE == "jruby" do
119
+ it "should not raise error with %{+format} syntax when @timestamp field is missing", :if => RUBY_ENGINE == "jruby" do
119
120
  str = "logstash-%{+YYYY}"
120
121
  subj = subject.clone
121
122
  subj.remove("[@timestamp]")
122
- expect{ subj.sprintf(str) }.to raise_error(LogStash::Error)
123
+ expect{ subj.sprintf(str) }.not_to raise_error(LogStash::Error)
123
124
  end
124
125
 
125
126
  it "should report fields with %{field} syntax" do
@@ -2,13 +2,14 @@
2
2
  require "spec_helper"
3
3
  require_relative "../../support/helpers"
4
4
  require_relative "../../support/matchers"
5
+ require "logstash/pipelines_registry"
5
6
  require "logstash/pipeline_action/create"
6
7
  require "logstash/inputs/generator"
7
8
 
8
9
  describe LogStash::PipelineAction::Create do
9
10
  let(:metric) { LogStash::Instrument::NullMetric.new(LogStash::Instrument::Collector.new) }
10
- let(:pipeline_config) { mock_pipeline_config(:main, "input { generator { id => '123' } } output { null {} }") }
11
- let(:pipelines) { java.util.concurrent.ConcurrentHashMap.new }
11
+ let(:pipeline_config) { mock_pipeline_config(:main, "input { dummyblockinginput { id => '123' } } output { null {} }") }
12
+ let(:pipelines) { LogStash::PipelinesRegistry.new }
12
13
  let(:agent) { double("agent") }
13
14
 
14
15
  before do
@@ -18,7 +19,7 @@ describe LogStash::PipelineAction::Create do
18
19
  subject { described_class.new(pipeline_config, metric) }
19
20
 
20
21
  after do
21
- pipelines.each do |_, pipeline|
22
+ pipelines.running_pipelines do |_, pipeline|
22
23
  pipeline.shutdown
23
24
  pipeline.thread.join
24
25
  end
@@ -44,7 +45,7 @@ describe LogStash::PipelineAction::Create do
44
45
 
45
46
  it "starts the pipeline" do
46
47
  subject.execute(agent, pipelines)
47
- expect(pipelines[:main].running?).to be_truthy
48
+ expect(pipelines.get_pipeline(:main).running?).to be_truthy
48
49
  end
49
50
 
50
51
  it "returns a successful execution status" do
@@ -54,7 +55,7 @@ describe LogStash::PipelineAction::Create do
54
55
 
55
56
  context "when the pipeline doesn't start" do
56
57
  context "with a syntax error" do
57
- let(:pipeline_config) { mock_pipeline_config(:main, "input { generator { id => '123' } } output { stdout ") } # bad syntax
58
+ let(:pipeline_config) { mock_pipeline_config(:main, "input { dummyblockinginput { id => '123' } } output { stdout ") } # bad syntax
58
59
 
59
60
  it "raises the exception upstream" do
60
61
  expect { subject.execute(agent, pipelines) }.to raise_error
@@ -62,7 +63,7 @@ describe LogStash::PipelineAction::Create do
62
63
  end
63
64
 
64
65
  context "with an error raised during `#register`" do
65
- let(:pipeline_config) { mock_pipeline_config(:main, "input { generator { id => '123' } } filter { ruby { init => '1/0' code => '1+2' } } output { null {} }") }
66
+ let(:pipeline_config) { mock_pipeline_config(:main, "input { dummyblockinginput { id => '123' } } filter { ruby { init => '1/0' code => '1+2' } } output { null {} }") }
66
67
 
67
68
  it "returns false" do
68
69
  expect(subject.execute(agent, pipelines)).not_to be_a_successful_action
@@ -71,8 +72,8 @@ describe LogStash::PipelineAction::Create do
71
72
  end
72
73
 
73
74
  context "when sorting create action" do
74
- let(:pipeline_config) { mock_pipeline_config(:main, "input { generator { id => '123' } } output { null {} }") }
75
- let(:system_pipeline_config) { mock_pipeline_config(:main_2, "input { generator { id => '123' } } output { null {} }", { "pipeline.system" => true }) }
75
+ let(:pipeline_config) { mock_pipeline_config(:main, "input { dummyblockinginput { id => '123' } } output { null {} }") }
76
+ let(:system_pipeline_config) { mock_pipeline_config(:main_2, "input { dummyblockinginput { id => '123' } } output { null {} }", { "pipeline.system" => true }) }
76
77
 
77
78
  it "should give higher priority to system pipeline" do
78
79
  action_user_pipeline = described_class.new(pipeline_config, metric)
@@ -2,15 +2,16 @@
2
2
  require "spec_helper"
3
3
  require_relative "../../support/helpers"
4
4
  require_relative "../../support/matchers"
5
+ require "logstash/pipelines_registry"
5
6
  require "logstash/pipeline_action/reload"
6
7
 
7
8
  describe LogStash::PipelineAction::Reload do
8
9
  let(:metric) { LogStash::Instrument::NullMetric.new(LogStash::Instrument::Collector.new) }
9
10
  let(:pipeline_id) { :main }
10
- let(:new_pipeline_config) { mock_pipeline_config(pipeline_id, "input { generator { id => 'new' } } output { null {} }", { "pipeline.reloadable" => true}) }
11
- let(:pipeline_config) { "input { generator {} } output { null {} }" }
11
+ let(:new_pipeline_config) { mock_pipeline_config(pipeline_id, "input { dummyblockinginput { id => 'new' } } output { null {} }", { "pipeline.reloadable" => true}) }
12
+ let(:pipeline_config) { "input { dummyblockinginput {} } output { null {} }" }
12
13
  let(:pipeline) { mock_pipeline_from_string(pipeline_config, mock_settings("pipeline.reloadable" => true)) }
13
- let(:pipelines) { chm = java.util.concurrent.ConcurrentHashMap.new; chm[pipeline_id] = pipeline; chm }
14
+ let(:pipelines) { r = LogStash::PipelinesRegistry.new; r.create_pipeline(pipeline_id, pipeline) { true }; r }
14
15
  let(:agent) { double("agent") }
15
16
 
16
17
  subject { described_class.new(new_pipeline_config, metric) }
@@ -21,7 +22,7 @@ describe LogStash::PipelineAction::Reload do
21
22
  end
22
23
 
23
24
  after do
24
- pipelines.each do |_, pipeline|
25
+ pipelines.running_pipelines do |_, pipeline|
25
26
  pipeline.shutdown
26
27
  pipeline.thread.join
27
28
  end
@@ -38,12 +39,12 @@ describe LogStash::PipelineAction::Reload do
38
39
 
39
40
  it "start the new pipeline" do
40
41
  subject.execute(agent, pipelines)
41
- expect(pipelines[pipeline_id].running?).to be_truthy
42
+ expect(pipelines.get_pipeline(pipeline_id).running?).to be_truthy
42
43
  end
43
44
 
44
45
  it "run the new pipeline code" do
45
46
  subject.execute(agent, pipelines)
46
- expect(pipelines[pipeline_id].config_hash).to eq(new_pipeline_config.config_hash)
47
+ expect(pipelines.get_pipeline(pipeline_id).config_hash).to eq(new_pipeline_config.config_hash)
47
48
  end
48
49
  end
49
50
 
@@ -58,7 +59,7 @@ describe LogStash::PipelineAction::Reload do
58
59
  end
59
60
 
60
61
  context "when the new pipeline is not reloadable" do
61
- let(:new_pipeline_config) { mock_pipeline_config(pipeline_id, "input { generator { id => 'new' } } output { null {} }", { "pipeline.reloadable" => false}) }
62
+ let(:new_pipeline_config) { mock_pipeline_config(pipeline_id, "input { dummyblockinginput { id => 'new' } } output { null {} }", { "pipeline.reloadable" => false}) }
62
63
 
63
64
  it "cannot successfully execute the action" do
64
65
  expect(subject.execute(agent, pipelines)).not_to be_a_successful_action
@@ -66,7 +67,7 @@ describe LogStash::PipelineAction::Reload do
66
67
  end
67
68
 
68
69
  context "when the new pipeline has syntax errors" do
69
- let(:new_pipeline_config) { mock_pipeline_config(pipeline_id, "input generator { id => 'new' } } output { null {} }", { "pipeline.reloadable" => false}) }
70
+ let(:new_pipeline_config) { mock_pipeline_config(pipeline_id, "input dummyblockinginput { id => 'new' } } output { null {} }", { "pipeline.reloadable" => false}) }
70
71
 
71
72
  it "cannot successfully execute the action" do
72
73
  expect(subject.execute(agent, pipelines)).not_to be_a_successful_action
@@ -75,7 +76,7 @@ describe LogStash::PipelineAction::Reload do
75
76
 
76
77
  context "when there is an error in the register" do
77
78
  before do
78
- allow_any_instance_of(LogStash::Inputs::Generator).to receive(:register).and_raise("Bad value")
79
+ allow_any_instance_of(LogStash::Inputs::DummyBlockingInput).to receive(:register).and_raise("Bad value")
79
80
  end
80
81
 
81
82
  it "cannot successfully execute the action" do
@@ -1,14 +1,15 @@
1
1
  # encoding: utf-8
2
2
  require "spec_helper"
3
3
  require_relative "../../support/helpers"
4
+ require "logstash/pipelines_registry"
4
5
  require "logstash/pipeline_action/stop"
5
6
  require "logstash/pipeline"
6
7
 
7
8
  describe LogStash::PipelineAction::Stop do
8
- let(:pipeline_config) { "input { generator {} } output { null {} }" }
9
+ let(:pipeline_config) { "input { dummyblockinginput {} } output { null {} }" }
9
10
  let(:pipeline_id) { :main }
10
11
  let(:pipeline) { mock_pipeline_from_string(pipeline_config) }
11
- let(:pipelines) { chm = java.util.concurrent.ConcurrentHashMap.new; chm[:main] = pipeline; chm }
12
+ let(:pipelines) { chm = LogStash::PipelinesRegistry.new; chm.create_pipeline(pipeline_id, pipeline) { true }; chm }
12
13
  let(:agent) { double("agent") }
13
14
 
14
15
  subject { described_class.new(pipeline_id) }
@@ -31,6 +32,6 @@ describe LogStash::PipelineAction::Stop do
31
32
  end
32
33
 
33
34
  it "removes the pipeline from the running pipelines" do
34
- expect { subject.execute(agent, pipelines) }.to change { pipelines.include?(pipeline_id) }.from(true).to(false)
35
+ expect { subject.execute(agent, pipelines) }.to change { pipelines.running_pipelines.keys }.from([:main]).to([])
35
36
  end
36
37
  end
@@ -0,0 +1,220 @@
1
+ # encoding: utf-8
2
+ require "spec_helper"
3
+ require "logstash/pipelines_registry"
4
+
5
+ describe LogStash::PipelinesRegistry do
6
+
7
+ let(:pipeline_id) { "test" }
8
+ let(:pipeline) { double("Pipeline") }
9
+ let (:logger) { double("Logger") }
10
+
11
+ context "at object creation" do
12
+ it "should be empty" do
13
+ expect(subject.size).to eq(0)
14
+ expect(subject.empty?).to be_truthy
15
+ expect(subject.running_pipelines).to be_empty
16
+ expect(subject.non_running_pipelines).to be_empty
17
+ expect(subject.running_user_defined_pipelines).to be_empty
18
+ end
19
+ end
20
+
21
+ context "creating a pipeline" do
22
+ context "without existing same pipeline id" do
23
+ it "registry should not have a state for pipeline_id" do
24
+ expect(subject.get_pipeline(pipeline_id)).to be_nil
25
+ end
26
+
27
+ it "should return block return value" do
28
+ expect(subject.create_pipeline(pipeline_id, pipeline) { "dummy" }).to eq("dummy")
29
+ end
30
+
31
+ it "should register the new pipeline upon successful create block" do
32
+ subject.create_pipeline(pipeline_id, pipeline) { true }
33
+ expect(subject.get_pipeline(pipeline_id)).to eq(pipeline)
34
+ end
35
+
36
+ it "should not register the new pipeline upon unsuccessful create block" do
37
+ subject.create_pipeline(pipeline_id, pipeline) { false }
38
+ expect(subject.get_pipeline(pipeline_id)).to be_nil
39
+ end
40
+ end
41
+
42
+ context "with existing pipeline id" do
43
+ before :each do
44
+ subject.create_pipeline(pipeline_id, pipeline) { true }
45
+ end
46
+
47
+ it "registry should have a state for pipeline_id" do
48
+ expect(subject.get_pipeline(pipeline_id)).to eq(pipeline)
49
+ end
50
+
51
+ context "when existing pipeline is not terminated" do
52
+ before :each do
53
+ expect(pipeline).to receive(:finished_execution?).and_return(false)
54
+ end
55
+
56
+ it "should return false" do
57
+ expect(subject.create_pipeline(pipeline_id, pipeline) { "dummy" }).to be_falsey
58
+ end
59
+
60
+ it "should not call block and log error if pipeline is not terminated" do
61
+ expect(LogStash::PipelinesRegistry).to receive(:logger).and_return(logger)
62
+ expect(logger).to receive(:error)
63
+ expect { |b| subject.create_pipeline(pipeline_id, pipeline, &b) }.not_to yield_control
64
+ end
65
+ end
66
+
67
+ context "when existing pipeline is terminated" do
68
+ let (:new_pipeline) { double("New Pipeline") }
69
+
70
+ before :each do
71
+ expect(pipeline).to receive(:finished_execution?).and_return(true)
72
+ end
73
+
74
+ it "should return block value" do
75
+ expect(subject.create_pipeline(pipeline_id, new_pipeline) { "dummy" }).to eq("dummy")
76
+ end
77
+
78
+ it "should return block value" do
79
+ expect(subject.create_pipeline(pipeline_id, new_pipeline) { "dummy" }).to eq("dummy")
80
+ end
81
+
82
+ it "should register new pipeline" do
83
+ subject.create_pipeline(pipeline_id, new_pipeline) { true }
84
+ expect(subject.get_pipeline(pipeline_id)).to eq(new_pipeline)
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ context "terminating a pipeline" do
91
+ context "without existing pipeline id" do
92
+ it "should log error" do
93
+ expect(LogStash::PipelinesRegistry).to receive(:logger).and_return(logger)
94
+ expect(logger).to receive(:error)
95
+ subject.terminate_pipeline(pipeline_id) { "dummy" }
96
+ end
97
+
98
+ it "should not yield to block" do
99
+ expect { |b| subject.terminate_pipeline(pipeline_id, &b) }.not_to yield_control
100
+ end
101
+ end
102
+
103
+ context "with existing pipeline id" do
104
+ before :each do
105
+ subject.create_pipeline(pipeline_id, pipeline) { true }
106
+ end
107
+
108
+ it "should yield to block" do
109
+ expect { |b| subject.terminate_pipeline(pipeline_id, &b) }.to yield_control
110
+ end
111
+
112
+ it "should keep pipeline id" do
113
+ subject.terminate_pipeline(pipeline_id) { "dummy" }
114
+ expect(subject.get_pipeline(pipeline_id)).to eq(pipeline)
115
+ end
116
+ end
117
+ end
118
+
119
+ context "reloading a pipeline" do
120
+ it "should log error with inexistent pipeline id" do
121
+ expect(LogStash::PipelinesRegistry).to receive(:logger).and_return(logger)
122
+ expect(logger).to receive(:error)
123
+ subject.reload_pipeline(pipeline_id) { }
124
+ end
125
+
126
+ context "with existing pipeline id" do
127
+ before :each do
128
+ subject.create_pipeline(pipeline_id, pipeline) { true }
129
+ end
130
+
131
+ it "should return block value" do
132
+ expect(subject.reload_pipeline(pipeline_id) { ["dummy", pipeline] }).to eq("dummy")
133
+ end
134
+
135
+ it "should not be terminated while reloading" do
136
+ expect(pipeline).to receive(:finished_execution?).and_return(false, true, true)
137
+
138
+ # 1st call: finished_execution? is false
139
+ expect(subject.running_pipelines).not_to be_empty
140
+
141
+ # 2nd call: finished_execution? is true
142
+ expect(subject.running_pipelines).to be_empty
143
+
144
+
145
+ queue = Queue.new # threadsafe queue
146
+ in_block = Concurrent::AtomicBoolean.new(false)
147
+
148
+ thread = Thread.new(subject, pipeline_id, pipeline, queue, in_block) do |subject, pipeline_id, pipeline, queue, in_block|
149
+ subject.reload_pipeline(pipeline_id) do
150
+ in_block.make_true
151
+ queue.pop
152
+ [true, pipeline]
153
+ end
154
+ end
155
+
156
+ # make sure we entered the block executioin
157
+ wait(10).for {in_block.true?}.to be_truthy
158
+
159
+ # at this point the thread is suspended waiting on queue
160
+
161
+ # since in reloading state, running_pipelines is not empty
162
+ expect(subject.running_pipelines).not_to be_empty
163
+
164
+ # unblock thread
165
+ queue.push(:dummy)
166
+ thread.join
167
+
168
+ # 3rd call: finished_execution? is true
169
+ expect(subject.running_pipelines).to be_empty
170
+ end
171
+ end
172
+ end
173
+
174
+ context "pipelines collections" do
175
+ context "with a non terminated pipelines" do
176
+ before :each do
177
+ subject.create_pipeline(pipeline_id, pipeline) { true }
178
+ expect(pipeline).to receive(:finished_execution?).and_return(false)
179
+ end
180
+
181
+ it "should find running pipelines" do
182
+ expect(subject.running_pipelines).not_to be_empty
183
+ end
184
+
185
+ it "should not find non_running pipelines" do
186
+ expect(subject.non_running_pipelines).to be_empty
187
+ end
188
+
189
+ it "should find running_user_defined_pipelines" do
190
+ expect(pipeline).to receive(:system?).and_return(false)
191
+ expect(subject.running_user_defined_pipelines).not_to be_empty
192
+ end
193
+
194
+ it "should not find running_user_defined_pipelines" do
195
+ expect(pipeline).to receive(:system?).and_return(true)
196
+ expect(subject.running_user_defined_pipelines).to be_empty
197
+ end
198
+ end
199
+
200
+ context "with a terminated pipelines" do
201
+ before :each do
202
+ subject.create_pipeline(pipeline_id, pipeline) { true }
203
+ expect(pipeline).to receive(:finished_execution?).and_return(true)
204
+ end
205
+
206
+ it "should not find running pipelines" do
207
+ expect(subject.running_pipelines).to be_empty
208
+ end
209
+
210
+ it "should find non_running pipelines" do
211
+ expect(subject.non_running_pipelines).not_to be_empty
212
+ end
213
+
214
+ it "should not find running_user_defined_pipelines" do
215
+ expect(subject.running_user_defined_pipelines).to be_empty
216
+ end
217
+ end
218
+
219
+ end
220
+ end
@@ -14,6 +14,7 @@ describe LogStash::QueueFactory do
14
14
  LogStash::Setting::Numeric.new("queue.checkpoint.acks", 1024),
15
15
  LogStash::Setting::Numeric.new("queue.checkpoint.writes", 1024),
16
16
  LogStash::Setting::Numeric.new("queue.checkpoint.interval", 1000),
17
+ LogStash::Setting::Boolean.new("queue.checkpoint.retry", false),
17
18
  LogStash::Setting::String.new("pipeline.id", pipeline_id),
18
19
  LogStash::Setting::PositiveInteger.new("pipeline.batch.size", 125),
19
20
  LogStash::Setting::PositiveInteger.new("pipeline.workers", LogStash::Config::CpuCoreStrategy.maximum)
@@ -46,7 +47,7 @@ describe LogStash::QueueFactory do
46
47
  let(:queue_path) { ::File.join(settings.get("path.queue"), pipeline_id) }
47
48
 
48
49
  after :each do
49
- FileUtils.rmdir(queue_path)
50
+ FileUtils.rm_rf(queue_path)
50
51
  end
51
52
 
52
53
  it "creates a queue directory based on the pipeline id" do