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.
- checksums.yaml +4 -4
- data/gemspec_jars.rb +4 -7
- data/lib/logstash-core/logstash-core.jar +0 -0
- data/lib/logstash-core/version.rb +4 -8
- data/lib/logstash-core_jars.rb +12 -26
- data/lib/logstash/agent.rb +261 -246
- data/lib/logstash/api/commands/default_metadata.rb +1 -1
- data/lib/logstash/api/commands/hot_threads_reporter.rb +5 -11
- data/lib/logstash/api/commands/node.rb +3 -2
- data/lib/logstash/api/commands/stats.rb +3 -2
- data/lib/logstash/bootstrap_check/bad_java.rb +16 -0
- data/lib/logstash/bootstrap_check/bad_ruby.rb +12 -0
- data/lib/logstash/bootstrap_check/default_config.rb +17 -0
- data/lib/logstash/compiler.rb +38 -0
- data/lib/logstash/compiler/lscl.rb +566 -0
- data/lib/logstash/compiler/lscl/lscl_grammar.rb +3503 -0
- data/lib/logstash/compiler/treetop_monkeypatches.rb +92 -0
- data/lib/logstash/config/config_ast.rb +4 -82
- data/lib/logstash/config/mixin.rb +73 -41
- data/lib/logstash/config/pipeline_config.rb +48 -0
- data/lib/logstash/config/source/base.rb +16 -0
- data/lib/logstash/config/source/local.rb +215 -0
- data/lib/logstash/config/source_loader.rb +125 -0
- data/lib/logstash/converge_result.rb +103 -0
- data/lib/logstash/environment.rb +6 -19
- data/lib/logstash/errors.rb +2 -0
- data/lib/logstash/execution_context.rb +4 -7
- data/lib/logstash/filter_delegator.rb +6 -9
- data/lib/logstash/inputs/base.rb +0 -2
- data/lib/logstash/instrument/collector.rb +5 -7
- data/lib/logstash/instrument/metric_store.rb +12 -12
- data/lib/logstash/instrument/metric_type/mean.rb +0 -5
- data/lib/logstash/instrument/namespaced_metric.rb +0 -4
- data/lib/logstash/instrument/namespaced_null_metric.rb +0 -4
- data/lib/logstash/instrument/null_metric.rb +0 -10
- data/lib/logstash/instrument/periodic_poller/cgroup.rb +85 -168
- data/lib/logstash/instrument/periodic_poller/jvm.rb +5 -5
- data/lib/logstash/instrument/periodic_poller/pq.rb +3 -7
- data/lib/logstash/instrument/periodic_pollers.rb +1 -3
- data/lib/logstash/instrument/wrapped_write_client.rb +24 -33
- data/lib/logstash/logging/logger.rb +15 -47
- data/lib/logstash/namespace.rb +0 -1
- data/lib/logstash/output_delegator.rb +5 -7
- data/lib/logstash/outputs/base.rb +0 -2
- data/lib/logstash/pipeline.rb +159 -87
- data/lib/logstash/pipeline_action.rb +13 -0
- data/lib/logstash/pipeline_action/base.rb +29 -0
- data/lib/logstash/pipeline_action/create.rb +47 -0
- data/lib/logstash/pipeline_action/reload.rb +48 -0
- data/lib/logstash/pipeline_action/stop.rb +23 -0
- data/lib/logstash/plugin.rb +0 -1
- data/lib/logstash/plugins/hooks_registry.rb +6 -0
- data/lib/logstash/plugins/registry.rb +0 -1
- data/lib/logstash/program.rb +14 -0
- data/lib/logstash/queue_factory.rb +5 -1
- data/lib/logstash/runner.rb +58 -80
- data/lib/logstash/settings.rb +3 -27
- data/lib/logstash/state_resolver.rb +41 -0
- data/lib/logstash/util/java_version.rb +6 -0
- data/lib/logstash/util/safe_uri.rb +12 -148
- data/lib/logstash/util/thread_dump.rb +4 -7
- data/lib/logstash/util/wrapped_acked_queue.rb +36 -39
- data/lib/logstash/util/wrapped_synchronous_queue.rb +29 -39
- data/lib/logstash/version.rb +10 -8
- data/locales/en.yml +3 -54
- data/logstash-core.gemspec +8 -35
- data/spec/{logstash/api/modules → api/lib/api}/logging_spec.rb +10 -1
- data/spec/{logstash/api/modules → api/lib/api}/node_plugins_spec.rb +2 -1
- data/spec/{logstash/api/modules → api/lib/api}/node_spec.rb +3 -3
- data/spec/{logstash/api/modules → api/lib/api}/node_stats_spec.rb +3 -7
- data/spec/{logstash/api/modules → api/lib/api}/plugins_spec.rb +3 -4
- data/spec/{logstash/api/modules → api/lib/api}/root_spec.rb +2 -2
- data/spec/api/lib/api/support/resource_dsl_methods.rb +87 -0
- data/spec/{logstash/api/commands/stats_spec.rb → api/lib/commands/stats.rb} +2 -7
- data/spec/{logstash/api → api/lib}/errors_spec.rb +1 -1
- data/spec/{logstash/api → api/lib}/rack_app_spec.rb +0 -0
- data/spec/api/spec_helper.rb +106 -0
- data/spec/logstash/agent/converge_spec.rb +286 -0
- data/spec/logstash/agent/metrics_spec.rb +244 -0
- data/spec/logstash/agent_spec.rb +213 -225
- data/spec/logstash/compiler/compiler_spec.rb +584 -0
- data/spec/logstash/config/config_ast_spec.rb +8 -47
- data/spec/logstash/config/mixin_spec.rb +2 -42
- data/spec/logstash/config/pipeline_config_spec.rb +75 -0
- data/spec/logstash/config/source/local_spec.rb +395 -0
- data/spec/logstash/config/source_loader_spec.rb +122 -0
- data/spec/logstash/converge_result_spec.rb +179 -0
- data/spec/logstash/event_spec.rb +0 -66
- data/spec/logstash/execution_context_spec.rb +8 -12
- data/spec/logstash/filter_delegator_spec.rb +12 -24
- data/spec/logstash/inputs/base_spec.rb +7 -5
- data/spec/logstash/instrument/periodic_poller/cgroup_spec.rb +92 -225
- data/spec/logstash/instrument/periodic_poller/jvm_spec.rb +1 -1
- data/spec/logstash/instrument/periodic_poller/os_spec.rb +32 -29
- data/spec/logstash/instrument/wrapped_write_client_spec.rb +33 -33
- data/spec/logstash/legacy_ruby_event_spec.rb +13 -4
- data/spec/logstash/output_delegator_spec.rb +11 -20
- data/spec/logstash/outputs/base_spec.rb +7 -5
- data/spec/logstash/pipeline_action/create_spec.rb +83 -0
- data/spec/logstash/pipeline_action/reload_spec.rb +83 -0
- data/spec/logstash/pipeline_action/stop_spec.rb +37 -0
- data/spec/logstash/pipeline_pq_file_spec.rb +1 -1
- data/spec/logstash/pipeline_spec.rb +81 -137
- data/spec/logstash/plugin_spec.rb +2 -1
- data/spec/logstash/plugins/hooks_registry_spec.rb +6 -0
- data/spec/logstash/queue_factory_spec.rb +13 -1
- data/spec/logstash/runner_spec.rb +29 -140
- data/spec/logstash/settings/writable_directory_spec.rb +10 -13
- data/spec/logstash/settings_spec.rb +0 -91
- data/spec/logstash/state_resolver_spec.rb +156 -0
- data/spec/logstash/timestamp_spec.rb +2 -6
- data/spec/logstash/util/java_version_spec.rb +22 -0
- data/spec/logstash/util/safe_uri_spec.rb +0 -56
- data/spec/logstash/util/wrapped_synchronous_queue_spec.rb +22 -0
- data/spec/support/helpers.rb +9 -11
- data/spec/support/matchers.rb +96 -6
- data/spec/support/mocks_classes.rb +80 -0
- data/spec/support/shared_contexts.rb +2 -27
- metadata +100 -149
- data/lib/logstash/config/loader.rb +0 -107
- data/lib/logstash/config/modules_common.rb +0 -103
- data/lib/logstash/config/source/modules.rb +0 -55
- data/lib/logstash/config/string_escape.rb +0 -27
- data/lib/logstash/dependency_report.rb +0 -131
- data/lib/logstash/dependency_report_runner.rb +0 -17
- data/lib/logstash/elasticsearch_client.rb +0 -142
- data/lib/logstash/instrument/global_metrics.rb +0 -13
- data/lib/logstash/instrument/periodic_poller/dlq.rb +0 -24
- data/lib/logstash/modules/cli_parser.rb +0 -74
- data/lib/logstash/modules/elasticsearch_config.rb +0 -22
- data/lib/logstash/modules/elasticsearch_importer.rb +0 -37
- data/lib/logstash/modules/elasticsearch_resource.rb +0 -10
- data/lib/logstash/modules/file_reader.rb +0 -36
- data/lib/logstash/modules/kibana_base.rb +0 -24
- data/lib/logstash/modules/kibana_client.rb +0 -124
- data/lib/logstash/modules/kibana_config.rb +0 -105
- data/lib/logstash/modules/kibana_dashboards.rb +0 -36
- data/lib/logstash/modules/kibana_importer.rb +0 -17
- data/lib/logstash/modules/kibana_resource.rb +0 -10
- data/lib/logstash/modules/kibana_settings.rb +0 -40
- data/lib/logstash/modules/logstash_config.rb +0 -120
- data/lib/logstash/modules/resource_base.rb +0 -38
- data/lib/logstash/modules/scaffold.rb +0 -52
- data/lib/logstash/modules/settings_merger.rb +0 -23
- data/lib/logstash/modules/util.rb +0 -17
- data/lib/logstash/util/dead_letter_queue_manager.rb +0 -61
- data/lib/logstash/util/environment_variables.rb +0 -43
- data/spec/logstash/config/loader_spec.rb +0 -38
- data/spec/logstash/config/string_escape_spec.rb +0 -24
- data/spec/logstash/instrument/periodic_poller/dlq_spec.rb +0 -17
- data/spec/logstash/modules/logstash_config_spec.rb +0 -56
- data/spec/logstash/modules/scaffold_spec.rb +0 -234
- data/spec/logstash/pipeline_dlq_commit_spec.rb +0 -109
- data/spec/logstash/settings/splittable_string_array_spec.rb +0 -51
- data/spec/logstash/util/wrapped_acked_queue_spec.rb +0 -49
- 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
|
data/spec/logstash/agent_spec.rb
CHANGED
@@ -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
|
-
|
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 {
|
19
|
-
let(:
|
20
|
-
|
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 "
|
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.
|
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 {
|
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(:
|
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
|
140
|
-
expect(subject).
|
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
|
150
|
-
context "with a
|
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(:
|
155
|
+
let(:mock_second_pipeline_config) { mock_pipeline_config(:main, second_pipeline_config, pipeline_settings) }
|
153
156
|
|
154
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
159
|
-
expect(subject).
|
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
|
-
|
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
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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) { { "
|
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
|
-
|
191
|
-
|
192
|
-
|
193
|
-
expect(subject).to
|
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
|
-
|
223
|
-
|
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
|
-
|
237
|
-
|
238
|
-
|
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
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
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
|
-
|
250
|
-
|
251
|
-
|
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
|
-
|
254
|
-
|
255
|
-
|
266
|
+
Stud.stop!(t)
|
267
|
+
t.join
|
268
|
+
subject.shutdown
|
269
|
+
end
|
270
|
+
end
|
256
271
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
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
|
-
|
273
|
-
|
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
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
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(:
|
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 "
|
303
|
-
|
304
|
-
|
305
|
-
|
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
|
-
|
314
|
-
|
315
|
-
} }
|
316
|
-
let(:
|
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
|
-
|
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.
|
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.
|
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
|
-
|
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 =>
|
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.
|
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
|
372
|
-
expect(subject).to
|
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
|
409
|
+
let(:temporary_file) { Stud::Temporary.file.path }
|
410
|
+
let(:config) { "input { generator { } } output { file { path => '#{temporary_file}' } }" }
|
406
411
|
|
407
|
-
let
|
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" =>
|
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
|
-
|
443
|
+
@t = Thread.new do
|
444
|
+
subject.execute
|
445
|
+
end
|
459
446
|
|
460
447
|
# wait for some events to reach the dummy_output
|
461
|
-
|
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(:
|
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
|
-
|
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
|
-
|
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.
|
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.
|
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
|