logstash-core 5.6.16-java → 6.0.0.alpha1-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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/gemspec_jars.rb +4 -7
  3. data/lib/logstash-core/logstash-core.jar +0 -0
  4. data/lib/logstash-core/version.rb +4 -8
  5. data/lib/logstash-core_jars.rb +12 -26
  6. data/lib/logstash/agent.rb +261 -246
  7. data/lib/logstash/api/commands/default_metadata.rb +1 -1
  8. data/lib/logstash/api/commands/hot_threads_reporter.rb +5 -11
  9. data/lib/logstash/api/commands/node.rb +3 -2
  10. data/lib/logstash/api/commands/stats.rb +3 -2
  11. data/lib/logstash/bootstrap_check/bad_java.rb +16 -0
  12. data/lib/logstash/bootstrap_check/bad_ruby.rb +12 -0
  13. data/lib/logstash/bootstrap_check/default_config.rb +17 -0
  14. data/lib/logstash/compiler.rb +38 -0
  15. data/lib/logstash/compiler/lscl.rb +566 -0
  16. data/lib/logstash/compiler/lscl/lscl_grammar.rb +3503 -0
  17. data/lib/logstash/compiler/treetop_monkeypatches.rb +92 -0
  18. data/lib/logstash/config/config_ast.rb +4 -82
  19. data/lib/logstash/config/mixin.rb +73 -41
  20. data/lib/logstash/config/pipeline_config.rb +48 -0
  21. data/lib/logstash/config/source/base.rb +16 -0
  22. data/lib/logstash/config/source/local.rb +215 -0
  23. data/lib/logstash/config/source_loader.rb +125 -0
  24. data/lib/logstash/converge_result.rb +103 -0
  25. data/lib/logstash/environment.rb +6 -19
  26. data/lib/logstash/errors.rb +2 -0
  27. data/lib/logstash/execution_context.rb +4 -7
  28. data/lib/logstash/filter_delegator.rb +6 -9
  29. data/lib/logstash/inputs/base.rb +0 -2
  30. data/lib/logstash/instrument/collector.rb +5 -7
  31. data/lib/logstash/instrument/metric_store.rb +12 -12
  32. data/lib/logstash/instrument/metric_type/mean.rb +0 -5
  33. data/lib/logstash/instrument/namespaced_metric.rb +0 -4
  34. data/lib/logstash/instrument/namespaced_null_metric.rb +0 -4
  35. data/lib/logstash/instrument/null_metric.rb +0 -10
  36. data/lib/logstash/instrument/periodic_poller/cgroup.rb +85 -168
  37. data/lib/logstash/instrument/periodic_poller/jvm.rb +5 -5
  38. data/lib/logstash/instrument/periodic_poller/pq.rb +3 -7
  39. data/lib/logstash/instrument/periodic_pollers.rb +1 -3
  40. data/lib/logstash/instrument/wrapped_write_client.rb +24 -33
  41. data/lib/logstash/logging/logger.rb +15 -47
  42. data/lib/logstash/namespace.rb +0 -1
  43. data/lib/logstash/output_delegator.rb +5 -7
  44. data/lib/logstash/outputs/base.rb +0 -2
  45. data/lib/logstash/pipeline.rb +159 -87
  46. data/lib/logstash/pipeline_action.rb +13 -0
  47. data/lib/logstash/pipeline_action/base.rb +29 -0
  48. data/lib/logstash/pipeline_action/create.rb +47 -0
  49. data/lib/logstash/pipeline_action/reload.rb +48 -0
  50. data/lib/logstash/pipeline_action/stop.rb +23 -0
  51. data/lib/logstash/plugin.rb +0 -1
  52. data/lib/logstash/plugins/hooks_registry.rb +6 -0
  53. data/lib/logstash/plugins/registry.rb +0 -1
  54. data/lib/logstash/program.rb +14 -0
  55. data/lib/logstash/queue_factory.rb +5 -1
  56. data/lib/logstash/runner.rb +58 -80
  57. data/lib/logstash/settings.rb +3 -27
  58. data/lib/logstash/state_resolver.rb +41 -0
  59. data/lib/logstash/util/java_version.rb +6 -0
  60. data/lib/logstash/util/safe_uri.rb +12 -148
  61. data/lib/logstash/util/thread_dump.rb +4 -7
  62. data/lib/logstash/util/wrapped_acked_queue.rb +36 -39
  63. data/lib/logstash/util/wrapped_synchronous_queue.rb +29 -39
  64. data/lib/logstash/version.rb +10 -8
  65. data/locales/en.yml +3 -54
  66. data/logstash-core.gemspec +8 -35
  67. data/spec/{logstash/api/modules → api/lib/api}/logging_spec.rb +10 -1
  68. data/spec/{logstash/api/modules → api/lib/api}/node_plugins_spec.rb +2 -1
  69. data/spec/{logstash/api/modules → api/lib/api}/node_spec.rb +3 -3
  70. data/spec/{logstash/api/modules → api/lib/api}/node_stats_spec.rb +3 -7
  71. data/spec/{logstash/api/modules → api/lib/api}/plugins_spec.rb +3 -4
  72. data/spec/{logstash/api/modules → api/lib/api}/root_spec.rb +2 -2
  73. data/spec/api/lib/api/support/resource_dsl_methods.rb +87 -0
  74. data/spec/{logstash/api/commands/stats_spec.rb → api/lib/commands/stats.rb} +2 -7
  75. data/spec/{logstash/api → api/lib}/errors_spec.rb +1 -1
  76. data/spec/{logstash/api → api/lib}/rack_app_spec.rb +0 -0
  77. data/spec/api/spec_helper.rb +106 -0
  78. data/spec/logstash/agent/converge_spec.rb +286 -0
  79. data/spec/logstash/agent/metrics_spec.rb +244 -0
  80. data/spec/logstash/agent_spec.rb +213 -225
  81. data/spec/logstash/compiler/compiler_spec.rb +584 -0
  82. data/spec/logstash/config/config_ast_spec.rb +8 -47
  83. data/spec/logstash/config/mixin_spec.rb +2 -42
  84. data/spec/logstash/config/pipeline_config_spec.rb +75 -0
  85. data/spec/logstash/config/source/local_spec.rb +395 -0
  86. data/spec/logstash/config/source_loader_spec.rb +122 -0
  87. data/spec/logstash/converge_result_spec.rb +179 -0
  88. data/spec/logstash/event_spec.rb +0 -66
  89. data/spec/logstash/execution_context_spec.rb +8 -12
  90. data/spec/logstash/filter_delegator_spec.rb +12 -24
  91. data/spec/logstash/inputs/base_spec.rb +7 -5
  92. data/spec/logstash/instrument/periodic_poller/cgroup_spec.rb +92 -225
  93. data/spec/logstash/instrument/periodic_poller/jvm_spec.rb +1 -1
  94. data/spec/logstash/instrument/periodic_poller/os_spec.rb +32 -29
  95. data/spec/logstash/instrument/wrapped_write_client_spec.rb +33 -33
  96. data/spec/logstash/legacy_ruby_event_spec.rb +13 -4
  97. data/spec/logstash/output_delegator_spec.rb +11 -20
  98. data/spec/logstash/outputs/base_spec.rb +7 -5
  99. data/spec/logstash/pipeline_action/create_spec.rb +83 -0
  100. data/spec/logstash/pipeline_action/reload_spec.rb +83 -0
  101. data/spec/logstash/pipeline_action/stop_spec.rb +37 -0
  102. data/spec/logstash/pipeline_pq_file_spec.rb +1 -1
  103. data/spec/logstash/pipeline_spec.rb +81 -137
  104. data/spec/logstash/plugin_spec.rb +2 -1
  105. data/spec/logstash/plugins/hooks_registry_spec.rb +6 -0
  106. data/spec/logstash/queue_factory_spec.rb +13 -1
  107. data/spec/logstash/runner_spec.rb +29 -140
  108. data/spec/logstash/settings/writable_directory_spec.rb +10 -13
  109. data/spec/logstash/settings_spec.rb +0 -91
  110. data/spec/logstash/state_resolver_spec.rb +156 -0
  111. data/spec/logstash/timestamp_spec.rb +2 -6
  112. data/spec/logstash/util/java_version_spec.rb +22 -0
  113. data/spec/logstash/util/safe_uri_spec.rb +0 -56
  114. data/spec/logstash/util/wrapped_synchronous_queue_spec.rb +22 -0
  115. data/spec/support/helpers.rb +9 -11
  116. data/spec/support/matchers.rb +96 -6
  117. data/spec/support/mocks_classes.rb +80 -0
  118. data/spec/support/shared_contexts.rb +2 -27
  119. metadata +100 -149
  120. data/lib/logstash/config/loader.rb +0 -107
  121. data/lib/logstash/config/modules_common.rb +0 -103
  122. data/lib/logstash/config/source/modules.rb +0 -55
  123. data/lib/logstash/config/string_escape.rb +0 -27
  124. data/lib/logstash/dependency_report.rb +0 -131
  125. data/lib/logstash/dependency_report_runner.rb +0 -17
  126. data/lib/logstash/elasticsearch_client.rb +0 -142
  127. data/lib/logstash/instrument/global_metrics.rb +0 -13
  128. data/lib/logstash/instrument/periodic_poller/dlq.rb +0 -24
  129. data/lib/logstash/modules/cli_parser.rb +0 -74
  130. data/lib/logstash/modules/elasticsearch_config.rb +0 -22
  131. data/lib/logstash/modules/elasticsearch_importer.rb +0 -37
  132. data/lib/logstash/modules/elasticsearch_resource.rb +0 -10
  133. data/lib/logstash/modules/file_reader.rb +0 -36
  134. data/lib/logstash/modules/kibana_base.rb +0 -24
  135. data/lib/logstash/modules/kibana_client.rb +0 -124
  136. data/lib/logstash/modules/kibana_config.rb +0 -105
  137. data/lib/logstash/modules/kibana_dashboards.rb +0 -36
  138. data/lib/logstash/modules/kibana_importer.rb +0 -17
  139. data/lib/logstash/modules/kibana_resource.rb +0 -10
  140. data/lib/logstash/modules/kibana_settings.rb +0 -40
  141. data/lib/logstash/modules/logstash_config.rb +0 -120
  142. data/lib/logstash/modules/resource_base.rb +0 -38
  143. data/lib/logstash/modules/scaffold.rb +0 -52
  144. data/lib/logstash/modules/settings_merger.rb +0 -23
  145. data/lib/logstash/modules/util.rb +0 -17
  146. data/lib/logstash/util/dead_letter_queue_manager.rb +0 -61
  147. data/lib/logstash/util/environment_variables.rb +0 -43
  148. data/spec/logstash/config/loader_spec.rb +0 -38
  149. data/spec/logstash/config/string_escape_spec.rb +0 -24
  150. data/spec/logstash/instrument/periodic_poller/dlq_spec.rb +0 -17
  151. data/spec/logstash/modules/logstash_config_spec.rb +0 -56
  152. data/spec/logstash/modules/scaffold_spec.rb +0 -234
  153. data/spec/logstash/pipeline_dlq_commit_spec.rb +0 -109
  154. data/spec/logstash/settings/splittable_string_array_spec.rb +0 -51
  155. data/spec/logstash/util/wrapped_acked_queue_spec.rb +0 -49
  156. data/versions-gem-copy.yml +0 -12
@@ -0,0 +1,244 @@
1
+ # encoding: utf-8
2
+ #
3
+ require "logstash/agent"
4
+ require_relative "../../support/helpers"
5
+ require_relative "../../support/matchers"
6
+ require_relative "../../support/mocks_classes"
7
+ require "spec_helper"
8
+
9
+ # Just make the tests a bit shorter to write and
10
+ # assert, I will keep theses methods here for easier understanding.
11
+ def mval(*path_elements)
12
+ mhash(*path_elements).value
13
+ end
14
+
15
+ def mhash(*path_elements)
16
+ metric.get_shallow(*path_elements)
17
+ end
18
+
19
+ describe LogStash::Agent do
20
+ # by default no tests uses the auto reload logic
21
+ let(:agent_settings) { mock_settings("config.reload.automatic" => false) }
22
+ let(:pipeline_settings) { { "pipeline.reloadable" => true } }
23
+
24
+ let(:pipeline_config) { mock_pipeline_config(:main, "input { generator {} } filter { mutate { add_tag => 'hello world' }} output { null {} }", pipeline_settings) }
25
+ let(:update_pipeline_config) { mock_pipeline_config(:main, "input { generator { id => 'new' } } output { null {} }", pipeline_settings) }
26
+ let(:bad_update_pipeline_config) { mock_pipeline_config(:main, "hooo }", pipeline_settings) }
27
+
28
+ let(:new_pipeline_config) { mock_pipeline_config(:new, "input { generator {} } output { null {} }", pipeline_settings) }
29
+ let(:bad_pipeline_config) { mock_pipeline_config(:bad, "hooo }", pipeline_settings) }
30
+ let(:second_bad_pipeline_config) { mock_pipeline_config(:second_bad, "hooo }", pipeline_settings) }
31
+
32
+ let(:source_loader) do
33
+ TestSourceLoader.new([])
34
+ end
35
+
36
+ subject { described_class.new(agent_settings, source_loader) }
37
+
38
+ before :each do
39
+ # This MUST run first, before `subject` is invoked to ensure clean state
40
+ clear_data_dir
41
+
42
+ # TODO(ph) until we decouple the webserver from the agent
43
+ # we just disable these calls
44
+ allow(subject).to receive(:start_webserver).and_return(false)
45
+ allow(subject).to receive(:stop_webserver).and_return(false)
46
+ end
47
+
48
+ # Lets make sure we stop all the running pipeline for every example
49
+ # so we don't have any rogue pipeline in the background
50
+ after :each do
51
+ subject.shutdown
52
+ end
53
+
54
+ let(:metric) { subject.metric.collector.snapshot_metric.metric_store }
55
+
56
+ context "when starting the agent" do
57
+ it "initialize the instance reload metrics" do
58
+ expect(mval(:stats, :reloads, :successes)).to eq(0)
59
+ expect(mval(:stats, :reloads, :failures)).to eq(0)
60
+ end
61
+ end
62
+
63
+ context "when we try to start one pipeline" do
64
+ context "and it succeed" do
65
+ let(:source_loader) do
66
+ TestSourceLoader.new(pipeline_config)
67
+ end
68
+
69
+ let(:pipeline_name) { :main }
70
+
71
+ it "doesnt changes the global successes" do
72
+ expect { subject.converge_state_and_update }.not_to change { mval(:stats, :reloads, :successes) }
73
+ end
74
+
75
+ it "doesn't change the failures" do
76
+ expect { subject.converge_state_and_update }.not_to change { mval(:stats, :reloads, :failures) }
77
+ end
78
+
79
+ it "sets the failures to 0" do
80
+ subject.converge_state_and_update
81
+ expect(mval(:stats, :pipelines, pipeline_name, :reloads, :failures)).to eq(0)
82
+ end
83
+
84
+ it "sets the successes to 0" do
85
+ subject.converge_state_and_update
86
+ expect(mval(:stats, :pipelines, pipeline_name, :reloads, :successes)).to eq(0)
87
+ end
88
+
89
+ it "sets the `last_error` to nil" do
90
+ subject.converge_state_and_update
91
+ expect(mval(:stats, :pipelines, pipeline_name, :reloads, :last_error)).to be_nil
92
+ end
93
+
94
+ it "sets the `last_failure_timestamp` to nil" do
95
+ subject.converge_state_and_update
96
+ expect(mval(:stats, :pipelines, :main, :reloads, :last_failure_timestamp)).to be_nil
97
+ end
98
+
99
+ it "sets the `last_success_timestamp` to nil" do
100
+ subject.converge_state_and_update
101
+ expect(mval(:stats, :pipelines, pipeline_name, :reloads, :last_success_timestamp)).to be_nil
102
+ end
103
+ end
104
+
105
+ context "and it fails" do
106
+ let(:source_loader) do
107
+ TestSourceLoader.new(bad_pipeline_config)
108
+ end
109
+
110
+ let(:pipeline_name) { :bad }
111
+
112
+ before do
113
+ subject.converge_state_and_update
114
+ end
115
+
116
+ it "doesnt changes the global successes" do
117
+ expect { subject.converge_state_and_update }.not_to change { mval(:stats, :reloads, :successes)}
118
+ end
119
+
120
+ it "doesn't change the failures" do
121
+ expect { subject.converge_state_and_update }.to change { mval(:stats, :reloads, :failures) }.by(1)
122
+ end
123
+
124
+ it "increments the pipeline failures" do
125
+ expect { subject.converge_state_and_update }.to change { mval(:stats, :pipelines, pipeline_name, :reloads, :failures) }.by(1)
126
+ end
127
+
128
+ it "sets the successes to 0" do
129
+ subject.converge_state_and_update
130
+ expect(mval(:stats, :pipelines, pipeline_name, :reloads, :successes)).to eq(0)
131
+ end
132
+
133
+ it "increase the global failures" do
134
+ expect { subject.converge_state_and_update }.to change { mval(:stats, :reloads, :failures) }
135
+ end
136
+
137
+ it "records the `last_error`" do
138
+ expect(mval(:stats, :pipelines, pipeline_name, :reloads, :last_error)).to_not be_nil
139
+ end
140
+
141
+ it "records the `message` and the `backtrace`" do
142
+ expect(mval(:stats, :pipelines, pipeline_name, :reloads, :last_error)[:message]).to_not be_nil
143
+ expect(mval(:stats, :pipelines, pipeline_name, :reloads, :last_error)[:backtrace]).to_not be_nil
144
+ end
145
+
146
+ it "records the time of the last failure" do
147
+ expect(mval(:stats, :pipelines, pipeline_name, :reloads, :last_failure_timestamp)).to_not be_nil
148
+ end
149
+
150
+ it "initializes the `last_success_timestamp`" do
151
+ expect(mval(:stats, :pipelines, pipeline_name, :reloads, :last_success_timestamp)).to be_nil
152
+ end
153
+ end
154
+
155
+ context "when we try to reload a pipeline" do
156
+ before do
157
+ # Running Pipeline to -> reload pipeline
158
+ expect(subject.converge_state_and_update.success?).to be_truthy
159
+ end
160
+
161
+ let(:pipeline_name) { :main }
162
+
163
+ context "and it succeed" do
164
+ let(:source_loader) do
165
+ TestSequenceSourceLoader.new(pipeline_config, update_pipeline_config)
166
+ end
167
+
168
+ it "increments the global successes" do
169
+ expect { subject.converge_state_and_update }.to change { mval(:stats, :reloads, :successes) }.by(1)
170
+ end
171
+
172
+ it "increment the pipeline successes" do
173
+ expect{ subject.converge_state_and_update }.to change { mval(:stats, :pipelines, pipeline_name, :reloads, :successes) }.by(1)
174
+ end
175
+
176
+ it "record the `last_success_timestamp`" do
177
+ expect(mval(:stats, :pipelines, pipeline_name, :reloads, :last_success_timestamp)).to be_nil
178
+ subject.converge_state_and_update
179
+ expect(mval(:stats, :pipelines, pipeline_name, :reloads, :last_success_timestamp)).not_to be_nil
180
+ end
181
+ end
182
+
183
+ context "and it fails" do
184
+ let(:source_loader) do
185
+ TestSequenceSourceLoader.new(pipeline_config, bad_update_pipeline_config)
186
+ end
187
+
188
+ it "increments the global failures" do
189
+ expect { subject.converge_state_and_update }.to change { mval(:stats, :reloads, :failures) }.by(1)
190
+ end
191
+
192
+ it "increment the pipeline failures" do
193
+ expect{ subject.converge_state_and_update }.to change { mval(:stats, :pipelines, pipeline_name, :reloads, :failures) }.by(1)
194
+ end
195
+ end
196
+ end
197
+
198
+ context "when we successfully reload a pipeline" do
199
+ let(:source_loader) do
200
+ TestSequenceSourceLoader.new(pipeline_config, update_pipeline_config)
201
+ end
202
+
203
+ before do
204
+ expect(subject.converge_state_and_update.success?).to be_truthy
205
+ end
206
+
207
+ it "it clear previous metrics" do
208
+ try(20) do
209
+ expect { mhash(:stats, :pipelines, :main, :plugins, :filters) }.not_to raise_error, "Filters stats should exist"
210
+ end
211
+ expect(subject.converge_state_and_update.success?).to be_truthy
212
+
213
+ # We do have to retry here, since stopping a pipeline is a blocking operation
214
+ expect { mhash(:stats, :pipelines, :main, :plugins, :filters) }.to raise_error
215
+ end
216
+ end
217
+
218
+ context "when we stop a pipeline" do
219
+ let(:source_loader) do
220
+ TestSequenceSourceLoader.new(pipeline_config, [])
221
+ end
222
+
223
+ before do
224
+ # Running Pipeline to -> reload pipeline
225
+ expect(subject.converge_state_and_update.success?).to be_truthy
226
+ end
227
+
228
+ it "clear pipeline specific metric" do
229
+ # since the pipeline is async, it can actually take some time to have metrics recordings
230
+ # so we try a few times
231
+ try(20) do
232
+ expect { mhash(:stats, :pipelines, :main, :events) }.not_to raise_error , "Events pipelien stats should exist"
233
+ expect { mhash(:stats, :pipelines, :main, :plugins) }.not_to raise_error, "Plugins pipeline stats should exist"
234
+ end
235
+
236
+ expect(subject.converge_state_and_update.success?).to be_truthy
237
+
238
+ # We do have to retry here, since stopping a pipeline is a blocking operation
239
+ expect { mhash(:stats, :pipelines, :main, :plugins) }.to raise_error
240
+ expect { mhash(:stats, :pipelines, :main, :events) }.to raise_error
241
+ end
242
+ end
243
+ end
244
+ end
@@ -2,30 +2,35 @@
2
2
  require "spec_helper"
3
3
  require "stud/temporary"
4
4
  require "logstash/inputs/generator"
5
+ require "logstash/config/pipeline_config"
6
+ require "logstash/config/source/local"
5
7
  require_relative "../support/mocks_classes"
6
8
  require "fileutils"
7
9
  require_relative "../support/helpers"
8
- require 'timeout'
10
+ require_relative "../support/matchers"
9
11
 
10
12
  describe LogStash::Agent do
11
-
12
- let(:agent_settings) { LogStash::SETTINGS }
13
+ let(:agent_settings) { mock_settings("config.string" => "input {}") }
13
14
  let(:default_pipeline_id) { LogStash::SETTINGS.get("pipeline.id") }
14
15
  let(:agent_args) { {} }
15
16
  let(:pipeline_settings) { agent_settings.clone }
16
17
  let(:pipeline_args) { {} }
17
18
  let(:config_file) { Stud::Temporary.pathname }
18
- let(:config_file_txt) { "input { generator { count => 100000 } } output { }" }
19
- let(:logger) { double("logger") }
20
- let(:timeout) {120} #seconds
19
+ let(:config_file_txt) { "input { generator { id => 'initial' } } output { }" }
20
+ let(:default_source_loader) do
21
+ sl = LogStash::Config::SourceLoader.new
22
+ sl.add_source(LogStash::Config::Source::Local.new(agent_settings))
23
+ sl
24
+ end
21
25
 
22
- subject { LogStash::Agent.new(agent_settings) }
26
+ subject { LogStash::Agent.new(agent_settings, default_source_loader) }
23
27
 
24
28
  before :each do
25
29
  # This MUST run first, before `subject` is invoked to ensure clean state
26
30
  clear_data_dir
27
31
 
28
32
  File.open(config_file, "w") { |f| f.puts config_file_txt }
33
+
29
34
  agent_args.each do |key, value|
30
35
  agent_settings.set(key, value)
31
36
  pipeline_settings.set(key, value)
@@ -33,9 +38,6 @@ describe LogStash::Agent do
33
38
  pipeline_args.each do |key, value|
34
39
  pipeline_settings.set(key, value)
35
40
  end
36
- allow(described_class).to receive(:logger).and_return(logger)
37
- [:debug, :info, :error, :warn, :fatal, :trace].each {|level| allow(logger).to receive(level) }
38
- [:debug?, :info?, :error?, :warn?, :fatal?, :trace?].each {|level| allow(logger).to receive(level) }
39
41
  end
40
42
 
41
43
  after :each do
@@ -44,10 +46,14 @@ describe LogStash::Agent do
44
46
  end
45
47
 
46
48
  it "fallback to hostname when no name is provided" do
47
- expect(LogStash::Agent.new.name).to eq(Socket.gethostname)
49
+ expect(LogStash::Agent.new(agent_settings, default_source_loader).name).to eq(Socket.gethostname)
50
+ end
51
+
52
+ after(:each) do
53
+ subject.shutdown # shutdown/close the pipelines
48
54
  end
49
55
 
50
- describe "register_pipeline" do
56
+ describe "adding a new pipeline" do
51
57
  let(:config_string) { "input { } filter { } output { }" }
52
58
  let(:agent_args) do
53
59
  {
@@ -58,16 +64,12 @@ describe LogStash::Agent do
58
64
  }
59
65
  end
60
66
 
61
- after(:each) do
62
- subject.close_pipelines
63
- end
64
-
65
67
  it "should delegate settings to new pipeline" do
66
68
  expect(LogStash::Pipeline).to receive(:new) do |arg1, arg2|
67
69
  expect(arg1).to eq(config_string)
68
70
  expect(arg2.to_hash).to include(agent_args)
69
71
  end
70
- subject.register_pipeline(agent_settings)
72
+ subject.converge_state_and_update
71
73
  end
72
74
  end
73
75
 
@@ -86,7 +88,11 @@ describe LogStash::Agent do
86
88
  end
87
89
 
88
90
  describe "#execute" do
89
- let(:config_file_txt) { "input { generator { count => 100000 } } output { }" }
91
+ let(:config_file_txt) { "input { generator { id => 'old'} } output { }" }
92
+ let(:mock_config_pipeline) { mock_pipeline_config(:main, config_file_txt, pipeline_settings) }
93
+
94
+ let(:source_loader) { TestSourceLoader.new(mock_config_pipeline) }
95
+ subject { described_class.new(agent_settings, source_loader) }
90
96
 
91
97
  before :each do
92
98
  allow(subject).to receive(:start_webserver).and_return(false)
@@ -101,99 +107,112 @@ describe LogStash::Agent do
101
107
  }
102
108
  end
103
109
 
104
- before(:each) do
105
- subject.register_pipeline(pipeline_settings)
106
- end
107
-
108
- context "when a system pipeline is running" do
109
- context "when one pipeline is finite" do
110
- let(:pipeline_args) {
111
- {
112
- "path.config" => "a",
113
- "config.string" => "input { generator { count => 1000 }} output { null {} }"
114
- }
115
- }
116
- let(:system_pipeline_settings) do
117
- s = agent_settings.clone
118
- s.set("path.config", "")
119
- s.set("config.string", "input { generator {}} output { null {} }")
120
- s.set("pipeline.id", ".monitoring")
121
- s.set("pipeline.system", true)
122
- s
123
- end
124
-
125
- it "stops logstash at the end of the execution of the finite pipeline" do
126
- subject.register_pipeline(system_pipeline_settings)
127
- expect(subject.execute).to be_nil
128
- end
129
- end
130
- end
131
110
 
132
111
  context "if state is clean" do
133
112
  before :each do
134
- allow(subject).to receive(:running_pipelines?).and_return(true)
135
- allow(subject).to receive(:sleep)
113
+ allow(subject).to receive(:running_user_defined_pipelines?).and_return(true)
136
114
  allow(subject).to receive(:clean_state?).and_return(false)
137
115
  end
138
116
 
139
- it "should not reload_state!" do
140
- expect(subject).to_not receive(:reload_state!)
117
+ it "should not converge state more than once" do
118
+ expect(subject).to receive(:converge_state_and_update).once
141
119
  t = Thread.new { subject.execute }
142
120
 
121
+ # TODO: refactor this. forcing an arbitrary fixed delay for thread concurrency issues is an indication of
122
+ # a bad test design or missing class functionality.
123
+ sleep(0.1)
143
124
  Stud.stop!(t)
144
125
  t.join
145
126
  subject.shutdown
146
127
  end
147
128
  end
148
129
 
149
- context "when calling reload_state!" do
150
- context "with a pipeline with auto reloading turned off" do
130
+ context "when calling reloading a pipeline" do
131
+ context "with a config that contains reload incompatible plugins" do
132
+ let(:second_pipeline_config) { "input { stdin {} } filter { } output { }" }
133
+ let(:mock_second_pipeline_config) { mock_pipeline_config(:main, second_pipeline_config, pipeline_settings) }
134
+
135
+ let(:source_loader) { TestSequenceSourceLoader.new(mock_config_pipeline, mock_second_pipeline_config)}
136
+
137
+ it "does not upgrade the new config" do
138
+ t = Thread.new { subject.execute }
139
+ sleep(0.1) until subject.running_pipelines? && subject.pipelines.values.first.ready?
140
+
141
+ expect(subject.converge_state_and_update).not_to be_a_successful_converge
142
+ expect(subject).to have_running_pipeline?(mock_config_pipeline)
143
+
144
+ # TODO: refactor this. forcing an arbitrary fixed delay for thread concurrency issues is an indication of
145
+ # a bad test design or missing class functionality.
146
+ sleep(0.1)
147
+ Stud.stop!(t)
148
+ t.join
149
+ subject.shutdown
150
+ end
151
+ end
152
+
153
+ context "with a config that does not contain reload incompatible plugins" do
151
154
  let(:second_pipeline_config) { "input { generator { } } filter { } output { }" }
152
- let(:pipeline_args) { { "config.reload.automatic" => false } }
155
+ let(:mock_second_pipeline_config) { mock_pipeline_config(:main, second_pipeline_config, pipeline_settings) }
153
156
 
154
- it "does not try to reload the pipeline" do
157
+ let(:source_loader) { TestSequenceSourceLoader.new(mock_config_pipeline, mock_second_pipeline_config)}
158
+
159
+ it "does upgrade the new config" do
155
160
  t = Thread.new { subject.execute }
156
- Timeout.timeout(timeout) do
157
- sleep(0.01) until subject.running_pipelines? && subject.pipelines.values.first.running?
158
- end
159
- expect(subject).to_not receive(:reload_pipeline!)
160
- File.open(config_file, "w") { |f| f.puts second_pipeline_config }
161
- subject.reload_state!
161
+ sleep(0.1) until subject.pipelines_count > 0 && subject.pipelines.values.first.ready?
162
+
163
+ expect(subject.converge_state_and_update).to be_a_successful_converge
164
+ expect(subject).to have_running_pipeline?(mock_second_pipeline_config)
162
165
 
163
166
  # TODO: refactor this. forcing an arbitrary fixed delay for thread concurrency issues is an indication of
164
167
  # a bad test design or missing class functionality.
168
+ sleep(0.1)
165
169
  Stud.stop!(t)
166
170
  t.join
167
171
  subject.shutdown
168
172
  end
173
+ end
174
+
175
+ end
176
+ context "when calling reload_state!" do
177
+ context "with a pipeline with auto reloading turned off" do
178
+ let(:second_pipeline_config) { "input { generator { } } filter { } output { }" }
179
+ let(:pipeline_args) { { "pipeline.reloadable" => false } }
180
+ let(:mock_second_pipeline_config) { mock_pipeline_config(:main, second_pipeline_config, mock_settings(pipeline_args)) }
169
181
 
170
- context "and force autoreload" do
171
- it "reloads the pipeline" do
172
- t = Thread.new { subject.execute }
173
- sleep(0.01) until subject.running_pipelines? && subject.pipelines.values.first.running?
174
- expect(subject).to receive(:reload_pipeline!).with("main", true)
175
- subject.reload_state!(true)
182
+ let(:source_loader) { TestSequenceSourceLoader.new(mock_config_pipeline, mock_second_pipeline_config)}
176
183
 
177
- Stud.stop!(t)
178
- t.join
179
- subject.shutdown
180
- end
184
+ it "does not try to reload the pipeline" do
185
+ t = Thread.new { subject.execute }
186
+ sleep(0.01) until subject.running_pipelines? && subject.pipelines.values.first.running?
187
+
188
+ expect(subject.converge_state_and_update).not_to be_a_successful_converge
189
+ expect(subject).to have_running_pipeline?(mock_config_pipeline)
190
+
191
+ # TODO: refactor this. forcing an arbitrary fixed delay for thread concurrency issues is an indication of
192
+ # a bad test design or missing class functionality.
193
+ sleep(0.1)
194
+ Stud.stop!(t)
195
+ t.join
196
+ subject.shutdown
181
197
  end
182
198
  end
183
199
 
184
200
  context "with a pipeline with auto reloading turned on" do
185
- let(:second_pipeline_config) { "input { generator { } } filter { } output { }" }
186
- let(:pipeline_args) { { "config.reload.automatic" => true } }
201
+ let(:second_pipeline_config) { "input { generator { id => 'second' } } filter { } output { }" }
202
+ let(:pipeline_args) { { "pipeline.reloadable" => true } }
203
+ let(:mock_second_pipeline_config) { mock_pipeline_config(:main, second_pipeline_config, mock_settings(pipeline_args)) }
204
+ let(:source_loader) { TestSequenceSourceLoader.new(mock_config_pipeline, mock_second_pipeline_config)}
187
205
 
188
206
  it "tries to reload the pipeline" do
189
207
  t = Thread.new { subject.execute }
190
- Timeout.timeout(timeout) do
191
- sleep(0.01) until subject.running_pipelines? && subject.pipelines.values.first.running?
192
- end
193
- expect(subject).to receive(:reload_pipeline!).once.and_call_original
194
- File.open(config_file, "w") { |f| f.puts second_pipeline_config }
195
- subject.reload_state!
208
+ sleep(0.01) until subject.running_pipelines? && subject.pipelines.values.first.running?
209
+
210
+ expect(subject.converge_state_and_update).to be_a_successful_converge
211
+ expect(subject).to have_running_pipeline?(mock_second_pipeline_config)
196
212
 
213
+ # TODO: refactor this. forcing an arbitrary fixed delay for thread concurrency issues is an indication of
214
+ # a bad test design or missing class functionality.
215
+ sleep(0.1)
197
216
  Stud.stop!(t)
198
217
  t.join
199
218
  subject.shutdown
@@ -203,91 +222,102 @@ describe LogStash::Agent do
203
222
  end
204
223
 
205
224
  context "when auto_reload is true" do
225
+ subject { described_class.new(agent_settings, default_source_loader) }
226
+
206
227
  let(:agent_args) do
207
228
  {
229
+ "config.string" => "",
208
230
  "config.reload.automatic" => true,
209
231
  "config.reload.interval" => 0.01,
210
- "path.config" => config_file,
232
+ "path.config" => config_file
211
233
  }
212
234
  end
213
235
 
214
- before(:each) do
215
- subject.register_pipeline(pipeline_settings)
216
- end
217
-
218
236
  context "if state is clean" do
219
237
  it "should periodically reload_state" do
220
238
  allow(subject).to receive(:clean_state?).and_return(false)
221
239
  t = Thread.new { subject.execute }
222
- Timeout.timeout(timeout) do
223
- sleep(0.01) until subject.running_pipelines? && subject.pipelines.values.first.running?
224
- end
225
- expect(subject).to receive(:reload_state!).at_least(2).times
226
-
227
- sleep 1
240
+ sleep(0.05) until subject.running_pipelines? && subject.pipelines.values.first.running?
241
+ expect(subject).to receive(:converge_state_and_update).at_least(2).times
228
242
 
243
+ # TODO: refactor this. forcing an arbitrary fixed delay for thread concurrency issues is an indication of
244
+ # a bad test design or missing class functionality.
245
+ sleep(0.1)
229
246
  Stud.stop!(t)
230
247
  t.join
231
248
  subject.shutdown
232
249
  end
233
250
  end
234
- end
235
251
 
236
- context "when calling reload_state!" do
237
- end
238
- end
252
+ context "when calling reload_state!" do
253
+ xcontext "with a config that contains reload incompatible plugins" do
254
+ let(:second_pipeline_config) { "input { stdin { id => '123' } } filter { } output { }" }
239
255
 
240
- describe "#reload_state!" do
241
- let(:first_pipeline_config) { "input { } filter { } output { }" }
242
- let(:second_pipeline_config) { "input { generator { count => 10000 } } filter { } output { }" }
243
- let(:pipeline_args) { {
244
- "config.string" => first_pipeline_config,
245
- "pipeline.workers" => 4,
246
- "config.reload.automatic" => true
247
- } }
256
+ it "does not upgrade the new config" do
257
+ t = Thread.new { subject.execute }
258
+ sleep(0.05) until subject.running_pipelines? && subject.pipelines.values.first.running?
259
+ File.open(config_file, "w") { |f| f.puts second_pipeline_config }
260
+ sleep(0.2) # lets us catch the new file
248
261
 
249
- before(:each) do
250
- subject.register_pipeline(pipeline_settings)
251
- end
262
+ try do
263
+ expect(subject.pipelines[default_pipeline_id.to_sym].config_str).not_to eq(second_pipeline_config)
264
+ end
252
265
 
253
- after(:each) do
254
- subject.close_pipelines
255
- end
266
+ Stud.stop!(t)
267
+ t.join
268
+ subject.shutdown
269
+ end
270
+ end
256
271
 
257
- context "when fetching a new state" do
258
- it "upgrades the state" do
259
- expect(subject).to receive(:fetch_config).and_return(second_pipeline_config)
260
- expect(subject).to receive(:upgrade_pipeline).with(default_pipeline_id, kind_of(LogStash::Settings), second_pipeline_config)
261
- subject.reload_state!
262
- end
263
- end
264
- context "when fetching the same state" do
265
- it "doesn't upgrade the state" do
266
- expect(subject).to receive(:fetch_config).and_return(first_pipeline_config)
267
- expect(subject).to_not receive(:upgrade_pipeline)
268
- subject.reload_state!
269
- end
270
- end
272
+ context "with a config that does not contain reload incompatible plugins" do
273
+ let(:second_pipeline_config) { "input { generator { id => 'new' } } filter { } output { }" }
274
+
275
+ it "does upgrade the new config" do
276
+ t = Thread.new { subject.execute }
277
+
278
+ sleep(0.05) until subject.running_pipelines? && subject.pipelines.values.first.running?
279
+
280
+ File.open(config_file, "w") { |f| f.puts second_pipeline_config }
281
+ sleep(5) # lets us catch the new file
271
282
 
272
- context "with a config that contains reload incompatible plugins" do
273
- let(:second_pipeline_config) { "input { stdin {} } filter { } output { }" }
283
+ try do
284
+ expect(subject.pipelines[default_pipeline_id.to_sym]).not_to be_nil
285
+ expect(subject.pipelines[default_pipeline_id.to_sym].config_str).to match(second_pipeline_config)
286
+ end
274
287
 
275
- it "does not upgrade the new config" do
276
- expect(subject).to receive(:fetch_config).and_return(second_pipeline_config)
277
- expect(subject).to_not receive(:upgrade_pipeline)
278
- subject.reload_state!
288
+ # TODO: refactor this. forcing an arbitrary fixed delay for thread concurrency issues is an indication of
289
+ # a bad test design or missing class functionality.
290
+ sleep(0.1)
291
+ Stud.stop!(t)
292
+ t.join
293
+ expect(subject.get_pipeline(:main).config_str).to match(second_pipeline_config)
294
+ subject.shutdown
295
+ end
296
+ end
279
297
  end
280
298
  end
281
299
  end
282
300
 
283
301
  describe "Environment Variables In Configs" do
284
- let(:pipeline_config) { "input { generator { message => '${FOO}-bar' } } filter { } output { }" }
302
+ let(:temporary_file) { Stud::Temporary.file.path }
303
+
304
+ let(:pipeline_config) { "input { generator { message => '${FOO}-bar' count => 1 } } filter { } output { file { path => '#{temporary_file}' } }" }
285
305
  let(:agent_args) { {
286
306
  "config.reload.automatic" => false,
287
307
  "config.reload.interval" => 0.01,
288
308
  "config.string" => pipeline_config
289
309
  } }
290
310
 
311
+ let(:source_loader) {
312
+ TestSourceLoader.new(mock_pipeline_config(default_pipeline_id, pipeline_config))
313
+ }
314
+
315
+ subject { described_class.new(mock_settings(agent_args), source_loader) }
316
+
317
+ after do
318
+ subject.shutdown
319
+ end
320
+
291
321
  context "environment variable templating" do
292
322
  before :each do
293
323
  @foo_content = ENV["FOO"]
@@ -296,52 +326,50 @@ describe LogStash::Agent do
296
326
 
297
327
  after :each do
298
328
  ENV["FOO"] = @foo_content
299
- subject.close_pipelines
300
329
  end
301
330
 
302
- it "doesn't upgrade the state" do
303
- allow(subject).to receive(:fetch_config).and_return(pipeline_config)
304
- subject.register_pipeline(pipeline_settings)
305
- expect(subject.pipelines[default_pipeline_id].inputs.first.message).to eq("foo-bar")
331
+ it "are evaluated at plugins creation" do
332
+ expect(subject.converge_state_and_update).to be_a_successful_converge
333
+
334
+ # Since the pipeline is running in another threads
335
+ # the content of the file wont be instant.
336
+ sleep(0.1) until ::File.size(temporary_file) > 0
337
+ json_document = LogStash::Json.load(File.read(temporary_file).chomp)
338
+ expect(json_document["message"]).to eq("foo-bar")
306
339
  end
307
340
  end
308
341
  end
309
342
 
310
343
  describe "#upgrade_pipeline" do
311
- let(:pipeline_config) { "input { } filter { } output { }" }
312
- let(:pipeline_args) { {
313
- "config.string" => pipeline_config,
314
- "pipeline.workers" => 4
315
- } }
316
- let(:new_pipeline_config) { "input { generator {} } output { }" }
344
+ let(:pipeline_config) { "input { generator {} } filter { } output { }" }
345
+ let(:pipeline_args) { { "pipeline.workers" => 4 } }
346
+ let(:mocked_pipeline_config) { mock_pipeline_config(default_pipeline_id, pipeline_config, mock_settings(pipeline_args))}
347
+
348
+ let(:new_pipeline_config) { "input generator {} } output { }" }
349
+ let(:mocked_new_pipeline_config) { mock_pipeline_config(default_pipeline_id, new_pipeline_config, mock_settings(pipeline_args))}
350
+ let(:source_loader) { TestSequenceSourceLoader.new(mocked_pipeline_config, mocked_new_pipeline_config)}
351
+
352
+ subject { described_class.new(agent_settings, source_loader) }
317
353
 
318
354
  before(:each) do
319
- subject.register_pipeline(pipeline_settings)
355
+ # Run the initial config
356
+ expect(subject.converge_state_and_update).to be_a_successful_converge
320
357
  end
321
358
 
322
359
  after(:each) do
323
360
  # new pipelines will be created part of the upgrade process so we need
324
361
  # to close any initialized pipelines
325
- subject.close_pipelines
362
+ subject.shutdown
326
363
  end
327
364
 
328
365
  context "when the upgrade fails" do
329
- before :each do
330
- allow(subject).to receive(:fetch_config).and_return(new_pipeline_config)
331
- allow(subject).to receive(:create_pipeline).and_return(nil)
332
- allow(subject).to receive(:stop_pipeline) do |id|
333
- # we register_pipeline but we never execute them so we have to mock #stop_pipeline to
334
- # not call Pipeline#shutdown but Pipeline#close
335
- subject.close_pipeline(id)
336
- end
337
- end
338
-
339
366
  it "leaves the state untouched" do
340
- subject.send(:"reload_pipeline!", default_pipeline_id)
367
+ expect(subject.converge_state_and_update).not_to be_a_successful_converge
341
368
  expect(subject.pipelines[default_pipeline_id].config_str).to eq(pipeline_config)
342
369
  end
343
370
 
344
- context "and current state is empty" do
371
+ # TODO(ph): This valid?
372
+ xcontext "and current state is empty" do
345
373
  it "should not start a pipeline" do
346
374
  expect(subject).to_not receive(:start_pipeline)
347
375
  subject.send(:"reload_pipeline!", default_pipeline_id)
@@ -350,45 +378,21 @@ describe LogStash::Agent do
350
378
  end
351
379
 
352
380
  context "when the upgrade succeeds" do
353
- let(:new_config) { "input { generator { count => 1 } } output { }" }
354
-
355
- before :each do
356
- allow(subject).to receive(:fetch_config).and_return(new_config)
357
- allow(subject).to receive(:start_pipeline).and_return(true)
358
- allow(subject).to receive(:stop_pipeline) do |id|
359
- # we register_pipeline but we never execute them so we have to mock #stop_pipeline to
360
- # not call Pipeline#shutdown but Pipeline#close
361
- subject.close_pipeline(id)
362
- end
363
- end
381
+ let(:new_config) { "input { generator { id => 'abc' count => 1000000 } } output { }" }
382
+ let(:mocked_new_pipeline_config) { mock_pipeline_config(default_pipeline_id, new_config, mock_settings(pipeline_args)) }
364
383
 
365
384
  it "updates the state" do
366
- subject.send(:"reload_pipeline!", default_pipeline_id)
385
+ expect(subject.converge_state_and_update).to be_a_successful_converge
367
386
  expect(subject.pipelines[default_pipeline_id].config_str).to eq(new_config)
368
387
  end
369
388
 
370
389
  it "starts the pipeline" do
371
- expect(subject).to receive(:start_pipeline).and_return(true)
372
- expect(subject).to receive(:stop_pipeline) do |id|
373
- # we register_pipeline but we never execute them so we have to mock #stop_pipeline to
374
- # not call Pipeline#shutdown but Pipeline#close
375
- subject.close_pipeline(id)
376
- end
377
- subject.send(:"reload_pipeline!", default_pipeline_id)
390
+ expect(subject.converge_state_and_update).to be_a_successful_converge
391
+ expect(subject.pipelines[default_pipeline_id].running?).to be_truthy
378
392
  end
379
393
  end
380
394
  end
381
395
 
382
- describe "#fetch_config" do
383
- let(:cli_config) { "filter { drop { } } " }
384
- let(:agent_args) { { "config.string" => cli_config, "path.config" => config_file } }
385
-
386
- it "should join the config string and config path content" do
387
- fetched_config = subject.send(:fetch_config, agent_settings)
388
- expect(fetched_config.strip).to eq(cli_config + IO.read(config_file).strip)
389
- end
390
- end
391
-
392
396
  context "#started_at" do
393
397
  it "return the start time when the agent is started" do
394
398
  expect(described_class::STARTED_AT).to be_kind_of(Time)
@@ -402,9 +406,10 @@ describe LogStash::Agent do
402
406
  end
403
407
 
404
408
  context "metrics after config reloading" do
405
- let!(:config) { "input { generator { } } output { dummyoutput { } }" }
409
+ let(:temporary_file) { Stud::Temporary.file.path }
410
+ let(:config) { "input { generator { } } output { file { path => '#{temporary_file}' } }" }
406
411
 
407
- let!(:config_path) do
412
+ let(:config_path) do
408
413
  f = Stud::Temporary.file
409
414
  f.write(config)
410
415
  f.fsync
@@ -412,55 +417,35 @@ describe LogStash::Agent do
412
417
  f.path
413
418
  end
414
419
 
415
- let(:pipeline_args) do
416
- {
417
- "pipeline.workers" => 2,
418
- "path.config" => config_path
419
- }
420
- end
421
-
422
420
  let(:agent_args) do
423
421
  {
424
- "config.reload.automatic" => false,
422
+ "config.reload.automatic" => true,
423
+ "config.reload.interval" => 0.01,
425
424
  "pipeline.batch.size" => 1,
426
- "metric.collect" => true
425
+ "metric.collect" => true,
426
+ "path.config" => config_path
427
427
  }
428
428
  end
429
429
 
430
- # We need to create theses dummy classes to know how many
431
- # events where actually generated by the pipeline and successfully send to the output.
432
- # Theses values are compared with what we store in the metric store.
433
- class DummyOutput2 < LogStash::Outputs::DroppingDummyOutput; end
434
-
435
- let!(:dummy_output) { LogStash::Outputs::DroppingDummyOutput.new }
436
- let!(:dummy_output2) { DummyOutput2.new }
437
430
  let(:initial_generator_threshold) { 1000 }
438
431
  let(:pipeline_thread) do
439
432
  Thread.new do
440
- subject.register_pipeline(pipeline_settings)
441
433
  subject.execute
442
434
  end
443
435
  end
444
436
 
437
+ subject { described_class.new(agent_settings, default_source_loader) }
445
438
 
446
439
  before :each do
447
- allow(LogStash::Outputs::DroppingDummyOutput).to receive(:new).at_least(:once).with(anything).and_return(dummy_output)
448
- allow(DummyOutput2).to receive(:new).at_least(:once).with(anything).and_return(dummy_output2)
449
-
450
- allow(LogStash::Plugin).to receive(:lookup).with("input", "generator").and_return(LogStash::Inputs::Generator)
451
- allow(LogStash::Plugin).to receive(:lookup).with("codec", "plain").and_return(LogStash::Codecs::Plain)
452
- allow(LogStash::Plugin).to receive(:lookup).with("output", "dummyoutput").and_return(LogStash::Outputs::DroppingDummyOutput)
453
- allow(LogStash::Plugin).to receive(:lookup).with("output", "dummyoutput2").and_return(DummyOutput2)
454
-
455
440
  @abort_on_exception = Thread.abort_on_exception
456
441
  Thread.abort_on_exception = true
457
442
 
458
- pipeline_thread
443
+ @t = Thread.new do
444
+ subject.execute
445
+ end
459
446
 
460
447
  # wait for some events to reach the dummy_output
461
- Timeout.timeout(timeout) do
462
- sleep(0.1) until dummy_output.events_received > initial_generator_threshold
463
- end
448
+ sleep(0.1) until IO.readlines(temporary_file).size > initial_generator_threshold
464
449
  end
465
450
 
466
451
  after :each do
@@ -468,8 +453,6 @@ describe LogStash::Agent do
468
453
  subject.shutdown
469
454
  Stud.stop!(pipeline_thread)
470
455
  pipeline_thread.join
471
- rescue
472
- #don't care about errors here.
473
456
  ensure
474
457
  Thread.abort_on_exception = @abort_on_exception
475
458
  end
@@ -477,7 +460,8 @@ describe LogStash::Agent do
477
460
 
478
461
  context "when reloading a good config" do
479
462
  let(:new_config_generator_counter) { 500 }
480
- let(:new_config) { "input { generator { count => #{new_config_generator_counter} } } output { dummyoutput2 {} }" }
463
+ let(:output_file) { Stud::Temporary.file.path }
464
+ let(:new_config) { "input { generator { count => #{new_config_generator_counter} } } output { file { path => '#{output_file}'} }" }
481
465
 
482
466
  before :each do
483
467
  File.open(config_path, "w") do |f|
@@ -485,12 +469,8 @@ describe LogStash::Agent do
485
469
  f.fsync
486
470
  end
487
471
 
488
- subject.send(:"reload_pipeline!", "main")
489
-
490
472
  # wait until pipeline restarts
491
- Timeout.timeout(timeout) do
492
- sleep(0.01) until dummy_output2.events_received > 0
493
- end
473
+ sleep(1) if ::File.read(output_file).empty?
494
474
  end
495
475
 
496
476
  it "resets the pipeline metric collector" do
@@ -509,8 +489,8 @@ describe LogStash::Agent do
509
489
  snapshot = subject.metric.collector.snapshot_metric
510
490
  value = snapshot.metric_store.get_with_path("/stats/pipelines")[:stats][:pipelines][:main][:reloads][:successes].value
511
491
  instance_value = snapshot.metric_store.get_with_path("/stats")[:stats][:reloads][:successes].value
512
- expect(value).to eq(1)
513
492
  expect(instance_value).to eq(1)
493
+ expect(value).to eq(1)
514
494
  end
515
495
 
516
496
  it "does not set the failure reload timestamp" do
@@ -541,7 +521,7 @@ describe LogStash::Agent do
541
521
  f.fsync
542
522
  end
543
523
 
544
- subject.send(:"reload_pipeline!", "main")
524
+ sleep(1)
545
525
  end
546
526
 
547
527
  it "does not increase the successful reload count" do
@@ -577,7 +557,15 @@ describe LogStash::Agent do
577
557
  end
578
558
 
579
559
  context "when reloading a config that raises exception on pipeline.run" do
580
- let(:new_config) { "input { generator { count => 10000 } }" }
560
+ let(:new_config) { "input { generator { count => 10000 } } output { null {} }" }
561
+ let(:agent_args) do
562
+ {
563
+ "config.reload.automatic" => false,
564
+ "pipeline.batch.size" => 1,
565
+ "metric.collect" => true,
566
+ "path.config" => config_path
567
+ }
568
+ end
581
569
 
582
570
  class BrokenGenerator < LogStash::Inputs::Generator
583
571
  def register
@@ -595,7 +583,7 @@ describe LogStash::Agent do
595
583
  end
596
584
 
597
585
  it "does not increase the successful reload count" do
598
- expect { subject.send(:"reload_pipeline!", "main") }.to_not change {
586
+ expect { subject.converge_state_and_update }.to_not change {
599
587
  snapshot = subject.metric.collector.snapshot_metric
600
588
  reload_metrics = snapshot.metric_store.get_with_path("/stats/pipelines")[:stats][:pipelines][:main][:reloads]
601
589
  reload_metrics[:successes].value
@@ -603,7 +591,7 @@ describe LogStash::Agent do
603
591
  end
604
592
 
605
593
  it "increases the failures reload count" do
606
- expect { subject.send(:"reload_pipeline!", "main") }.to change {
594
+ expect { subject.converge_state_and_update }.to change {
607
595
  snapshot = subject.metric.collector.snapshot_metric
608
596
  reload_metrics = snapshot.metric_store.get_with_path("/stats/pipelines")[:stats][:pipelines][:main][:reloads]
609
597
  reload_metrics[:failures].value