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.
- 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
|