logstash-core 5.6.16-java → 6.0.0.alpha1-java

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