logstash-output-elasticsearch-test 10.3.0-x86_64-linux
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 +7 -0
- data/CHANGELOG.md +397 -0
- data/CONTRIBUTORS +33 -0
- data/Gemfile +15 -0
- data/LICENSE +13 -0
- data/NOTICE.TXT +5 -0
- data/README.md +106 -0
- data/docs/index.asciidoc +899 -0
- data/lib/logstash/outputs/elasticsearch/common.rb +441 -0
- data/lib/logstash/outputs/elasticsearch/common_configs.rb +167 -0
- data/lib/logstash/outputs/elasticsearch/default-ilm-policy.json +14 -0
- data/lib/logstash/outputs/elasticsearch/elasticsearch-template-es2x.json +95 -0
- data/lib/logstash/outputs/elasticsearch/elasticsearch-template-es5x.json +46 -0
- data/lib/logstash/outputs/elasticsearch/elasticsearch-template-es6x.json +45 -0
- data/lib/logstash/outputs/elasticsearch/elasticsearch-template-es7x.json +44 -0
- data/lib/logstash/outputs/elasticsearch/elasticsearch-template-es8x.json +44 -0
- data/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb +131 -0
- data/lib/logstash/outputs/elasticsearch/http_client/pool.rb +495 -0
- data/lib/logstash/outputs/elasticsearch/http_client.rb +432 -0
- data/lib/logstash/outputs/elasticsearch/http_client_builder.rb +159 -0
- data/lib/logstash/outputs/elasticsearch/ilm.rb +113 -0
- data/lib/logstash/outputs/elasticsearch/template_manager.rb +61 -0
- data/lib/logstash/outputs/elasticsearch.rb +263 -0
- data/logstash-output-elasticsearch.gemspec +33 -0
- data/spec/es_spec_helper.rb +189 -0
- data/spec/fixtures/_nodes/2x_1x.json +27 -0
- data/spec/fixtures/_nodes/5x_6x.json +81 -0
- data/spec/fixtures/_nodes/7x.json +92 -0
- data/spec/fixtures/htpasswd +2 -0
- data/spec/fixtures/nginx_reverse_proxy.conf +22 -0
- data/spec/fixtures/scripts/groovy/scripted_update.groovy +2 -0
- data/spec/fixtures/scripts/groovy/scripted_update_nested.groovy +2 -0
- data/spec/fixtures/scripts/groovy/scripted_upsert.groovy +2 -0
- data/spec/fixtures/scripts/painless/scripted_update.painless +2 -0
- data/spec/fixtures/scripts/painless/scripted_update_nested.painless +1 -0
- data/spec/fixtures/scripts/painless/scripted_upsert.painless +1 -0
- data/spec/fixtures/template-with-policy-es6x.json +48 -0
- data/spec/fixtures/template-with-policy-es7x.json +45 -0
- data/spec/fixtures/test_certs/ca/ca.crt +32 -0
- data/spec/fixtures/test_certs/ca/ca.key +51 -0
- data/spec/fixtures/test_certs/test.crt +36 -0
- data/spec/fixtures/test_certs/test.key +51 -0
- data/spec/integration/outputs/compressed_indexing_spec.rb +69 -0
- data/spec/integration/outputs/create_spec.rb +67 -0
- data/spec/integration/outputs/delete_spec.rb +65 -0
- data/spec/integration/outputs/groovy_update_spec.rb +150 -0
- data/spec/integration/outputs/ilm_spec.rb +531 -0
- data/spec/integration/outputs/index_spec.rb +178 -0
- data/spec/integration/outputs/index_version_spec.rb +102 -0
- data/spec/integration/outputs/ingest_pipeline_spec.rb +74 -0
- data/spec/integration/outputs/metrics_spec.rb +70 -0
- data/spec/integration/outputs/no_es_on_startup_spec.rb +58 -0
- data/spec/integration/outputs/painless_update_spec.rb +189 -0
- data/spec/integration/outputs/parent_spec.rb +102 -0
- data/spec/integration/outputs/retry_spec.rb +169 -0
- data/spec/integration/outputs/routing_spec.rb +61 -0
- data/spec/integration/outputs/sniffer_spec.rb +133 -0
- data/spec/integration/outputs/templates_5x_spec.rb +98 -0
- data/spec/integration/outputs/templates_spec.rb +98 -0
- data/spec/integration/outputs/update_spec.rb +116 -0
- data/spec/support/elasticsearch/api/actions/delete_ilm_policy.rb +19 -0
- data/spec/support/elasticsearch/api/actions/get_alias.rb +18 -0
- data/spec/support/elasticsearch/api/actions/get_ilm_policy.rb +18 -0
- data/spec/support/elasticsearch/api/actions/put_alias.rb +24 -0
- data/spec/support/elasticsearch/api/actions/put_ilm_policy.rb +25 -0
- data/spec/unit/http_client_builder_spec.rb +185 -0
- data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +149 -0
- data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +274 -0
- data/spec/unit/outputs/elasticsearch/http_client_spec.rb +250 -0
- data/spec/unit/outputs/elasticsearch/template_manager_spec.rb +25 -0
- data/spec/unit/outputs/elasticsearch_proxy_spec.rb +72 -0
- data/spec/unit/outputs/elasticsearch_spec.rb +675 -0
- data/spec/unit/outputs/elasticsearch_ssl_spec.rb +82 -0
- data/spec/unit/outputs/error_whitelist_spec.rb +54 -0
- metadata +300 -0
@@ -0,0 +1,178 @@
|
|
1
|
+
require_relative "../../../spec/es_spec_helper"
|
2
|
+
require "logstash/outputs/elasticsearch"
|
3
|
+
|
4
|
+
describe "TARGET_BULK_BYTES", :integration => true do
|
5
|
+
let(:target_bulk_bytes) { LogStash::Outputs::ElasticSearch::TARGET_BULK_BYTES }
|
6
|
+
let(:event_count) { 1000 }
|
7
|
+
let(:events) { event_count.times.map { event }.to_a }
|
8
|
+
let(:config) {
|
9
|
+
{
|
10
|
+
"hosts" => get_host_port,
|
11
|
+
"index" => index
|
12
|
+
}
|
13
|
+
}
|
14
|
+
let(:index) { 10.times.collect { rand(10).to_s }.join("") }
|
15
|
+
let(:type) { ESHelper.es_version_satisfies?("< 7") ? "doc" : "_doc" }
|
16
|
+
|
17
|
+
subject { LogStash::Outputs::ElasticSearch.new(config) }
|
18
|
+
|
19
|
+
before do
|
20
|
+
subject.register
|
21
|
+
allow(subject.client).to receive(:bulk_send).with(any_args).and_call_original
|
22
|
+
subject.multi_receive(events)
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "batches that are too large for one" do
|
26
|
+
let(:event) { LogStash::Event.new("message" => "a " * (((target_bulk_bytes/2) / event_count)+1)) }
|
27
|
+
|
28
|
+
it "should send in two batches" do
|
29
|
+
expect(subject.client).to have_received(:bulk_send).twice do |payload|
|
30
|
+
expect(payload.size).to be <= target_bulk_bytes
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "batches that fit in one" do
|
35
|
+
# Normally you'd want to generate a request that's just 1 byte below the limit, but it's
|
36
|
+
# impossible to know how many bytes an event will serialize as with bulk proto overhead
|
37
|
+
let(:event) { LogStash::Event.new("message" => "a") }
|
38
|
+
|
39
|
+
it "should send in one batch" do
|
40
|
+
expect(subject.client).to have_received(:bulk_send).once do |payload|
|
41
|
+
expect(payload.size).to be <= target_bulk_bytes
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "indexing" do
|
49
|
+
let(:event) { LogStash::Event.new("message" => "Hello World!", "type" => type) }
|
50
|
+
let(:index) { 10.times.collect { rand(10).to_s }.join("") }
|
51
|
+
let(:type) { ESHelper.es_version_satisfies?("< 7") ? "doc" : "_doc" }
|
52
|
+
let(:event_count) { 1 + rand(2) }
|
53
|
+
let(:config) { "not implemented" }
|
54
|
+
let(:events) { event_count.times.map { event }.to_a }
|
55
|
+
subject { LogStash::Outputs::ElasticSearch.new(config) }
|
56
|
+
|
57
|
+
let(:es_url) { "http://#{get_host_port}" }
|
58
|
+
let(:index_url) {"#{es_url}/#{index}"}
|
59
|
+
let(:http_client_options) { {} }
|
60
|
+
let(:http_client) do
|
61
|
+
Manticore::Client.new(http_client_options)
|
62
|
+
end
|
63
|
+
|
64
|
+
before do
|
65
|
+
subject.register
|
66
|
+
subject.multi_receive([])
|
67
|
+
end
|
68
|
+
|
69
|
+
shared_examples "an indexer" do |secure|
|
70
|
+
it "ships events" do
|
71
|
+
subject.multi_receive(events)
|
72
|
+
|
73
|
+
http_client.post("#{es_url}/_refresh").call
|
74
|
+
|
75
|
+
response = http_client.get("#{index_url}/_count?q=*")
|
76
|
+
result = LogStash::Json.load(response.body)
|
77
|
+
cur_count = result["count"]
|
78
|
+
expect(cur_count).to eq(event_count)
|
79
|
+
|
80
|
+
response = http_client.get("#{index_url}/_search?q=*&size=1000")
|
81
|
+
result = LogStash::Json.load(response.body)
|
82
|
+
result["hits"]["hits"].each do |doc|
|
83
|
+
expect(doc["_type"]).to eq(type) if ESHelper.es_version_satisfies?(">= 6", "< 8")
|
84
|
+
expect(doc).not_to include("_type") if ESHelper.es_version_satisfies?(">= 8")
|
85
|
+
expect(doc["_index"]).to eq(index)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it "sets the correct content-type header" do
|
90
|
+
expected_manticore_opts = {:headers => {"Content-Type" => "application/json"}, :body => anything}
|
91
|
+
if secure
|
92
|
+
expected_manticore_opts = {
|
93
|
+
:headers => {"Content-Type" => "application/json"},
|
94
|
+
:body => anything,
|
95
|
+
:auth => {
|
96
|
+
:user => user,
|
97
|
+
:password => password,
|
98
|
+
:eager => true
|
99
|
+
}}
|
100
|
+
end
|
101
|
+
expect(subject.client.pool.adapter.client).to receive(:send).
|
102
|
+
with(anything, anything, expected_manticore_opts).at_least(:once).
|
103
|
+
and_call_original
|
104
|
+
subject.multi_receive(events)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "an indexer with custom index_type", :integration => true do
|
109
|
+
let(:config) {
|
110
|
+
{
|
111
|
+
"hosts" => get_host_port,
|
112
|
+
"index" => index
|
113
|
+
}
|
114
|
+
}
|
115
|
+
it_behaves_like("an indexer")
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "an indexer with no type value set (default to doc)", :integration => true do
|
119
|
+
let(:type) { ESHelper.es_version_satisfies?("< 7") ? "doc" : "_doc" }
|
120
|
+
let(:config) {
|
121
|
+
{
|
122
|
+
"hosts" => get_host_port,
|
123
|
+
"index" => index
|
124
|
+
}
|
125
|
+
}
|
126
|
+
it_behaves_like("an indexer")
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "a secured indexer", :secure_integration => true do
|
130
|
+
let(:user) { "simpleuser" }
|
131
|
+
let(:password) { "abc123" }
|
132
|
+
let(:cacert) { "spec/fixtures/test_certs/test.crt" }
|
133
|
+
let(:es_url) {"https://elasticsearch:9200"}
|
134
|
+
let(:config) do
|
135
|
+
{
|
136
|
+
"hosts" => ["elasticsearch:9200"],
|
137
|
+
"user" => user,
|
138
|
+
"password" => password,
|
139
|
+
"ssl" => true,
|
140
|
+
"cacert" => "spec/fixtures/test_certs/test.crt",
|
141
|
+
"index" => index
|
142
|
+
}
|
143
|
+
end
|
144
|
+
let(:http_client_options) do
|
145
|
+
{
|
146
|
+
:auth => {
|
147
|
+
:user => user,
|
148
|
+
:password => password
|
149
|
+
},
|
150
|
+
:ssl => {
|
151
|
+
:enabled => true,
|
152
|
+
:ca_file => cacert
|
153
|
+
}
|
154
|
+
}
|
155
|
+
end
|
156
|
+
it_behaves_like("an indexer", true)
|
157
|
+
|
158
|
+
describe "with a password requiring escaping" do
|
159
|
+
let(:user) { "f@ncyuser" }
|
160
|
+
let(:password) { "ab%12#" }
|
161
|
+
|
162
|
+
include_examples("an indexer", true)
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "with a user/password requiring escaping in the URL" do
|
166
|
+
let(:config) do
|
167
|
+
{
|
168
|
+
"hosts" => ["https://#{CGI.escape(user)}:#{CGI.escape(password)}@elasticsearch:9200"],
|
169
|
+
"ssl" => true,
|
170
|
+
"cacert" => "spec/fixtures/test_certs/test.crt",
|
171
|
+
"index" => index
|
172
|
+
}
|
173
|
+
end
|
174
|
+
|
175
|
+
include_examples("an indexer", true)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require_relative "../../../spec/es_spec_helper"
|
2
|
+
require "logstash/outputs/elasticsearch"
|
3
|
+
|
4
|
+
if ESHelper.es_version_satisfies?(">= 2")
|
5
|
+
describe "Versioned indexing", :integration => true do
|
6
|
+
require "logstash/outputs/elasticsearch"
|
7
|
+
|
8
|
+
let(:es) { get_client }
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
# Delete all templates first.
|
12
|
+
# Clean ES of data before we start.
|
13
|
+
es.indices.delete_template(:name => "*")
|
14
|
+
# This can fail if there are no indexes, ignore failure.
|
15
|
+
es.indices.delete(:index => "*") rescue nil
|
16
|
+
es.indices.refresh
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when index only" do
|
20
|
+
subject { LogStash::Outputs::ElasticSearch.new(settings) }
|
21
|
+
|
22
|
+
before do
|
23
|
+
subject.register
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "unversioned output" do
|
27
|
+
let(:settings) do
|
28
|
+
{
|
29
|
+
"manage_template" => true,
|
30
|
+
"index" => "logstash-index",
|
31
|
+
"template_overwrite" => true,
|
32
|
+
"hosts" => get_host_port(),
|
33
|
+
"action" => "index",
|
34
|
+
"script_lang" => "groovy",
|
35
|
+
"document_id" => "%{my_id}"
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should default to ES version" do
|
40
|
+
subject.multi_receive([LogStash::Event.new("my_id" => "123", "message" => "foo")])
|
41
|
+
r = es.get(:index => 'logstash-index', :type => doc_type, :id => "123", :refresh => true)
|
42
|
+
expect(r["_version"]).to eq(1)
|
43
|
+
expect(r["_source"]["message"]).to eq('foo')
|
44
|
+
subject.multi_receive([LogStash::Event.new("my_id" => "123", "message" => "foobar")])
|
45
|
+
r2 = es.get(:index => 'logstash-index', :type => doc_type, :id => "123", :refresh => true)
|
46
|
+
expect(r2["_version"]).to eq(2)
|
47
|
+
expect(r2["_source"]["message"]).to eq('foobar')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "versioned output" do
|
52
|
+
let(:settings) do
|
53
|
+
{
|
54
|
+
"manage_template" => true,
|
55
|
+
"index" => "logstash-index",
|
56
|
+
"template_overwrite" => true,
|
57
|
+
"hosts" => get_host_port(),
|
58
|
+
"action" => "index",
|
59
|
+
"script_lang" => "groovy",
|
60
|
+
"document_id" => "%{my_id}",
|
61
|
+
"version" => "%{my_version}",
|
62
|
+
"version_type" => "external",
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should respect the external version" do
|
67
|
+
id = "ev1"
|
68
|
+
subject.multi_receive([LogStash::Event.new("my_id" => id, "my_version" => "99", "message" => "foo")])
|
69
|
+
r = es.get(:index => 'logstash-index', :type => doc_type, :id => id, :refresh => true)
|
70
|
+
expect(r["_version"]).to eq(99)
|
71
|
+
expect(r["_source"]["message"]).to eq('foo')
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should ignore non-monotonic external version updates" do
|
75
|
+
id = "ev2"
|
76
|
+
subject.multi_receive([LogStash::Event.new("my_id" => id, "my_version" => "99", "message" => "foo")])
|
77
|
+
r = es.get(:index => 'logstash-index', :type => doc_type, :id => id, :refresh => true)
|
78
|
+
expect(r["_version"]).to eq(99)
|
79
|
+
expect(r["_source"]["message"]).to eq('foo')
|
80
|
+
|
81
|
+
subject.multi_receive([LogStash::Event.new("my_id" => id, "my_version" => "98", "message" => "foo")])
|
82
|
+
r2 = es.get(:index => 'logstash-index', :type => doc_type, :id => id, :refresh => true)
|
83
|
+
expect(r2["_version"]).to eq(99)
|
84
|
+
expect(r2["_source"]["message"]).to eq('foo')
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should commit monotonic external version updates" do
|
88
|
+
id = "ev3"
|
89
|
+
subject.multi_receive([LogStash::Event.new("my_id" => id, "my_version" => "99", "message" => "foo")])
|
90
|
+
r = es.get(:index => 'logstash-index', :type => doc_type, :id => id, :refresh => true)
|
91
|
+
expect(r["_version"]).to eq(99)
|
92
|
+
expect(r["_source"]["message"]).to eq('foo')
|
93
|
+
|
94
|
+
subject.multi_receive([LogStash::Event.new("my_id" => id, "my_version" => "100", "message" => "foo")])
|
95
|
+
r2 = es.get(:index => 'logstash-index', :type => doc_type, :id => id, :refresh => true)
|
96
|
+
expect(r2["_version"]).to eq(100)
|
97
|
+
expect(r2["_source"]["message"]).to eq('foo')
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require_relative "../../../spec/es_spec_helper"
|
2
|
+
|
3
|
+
if ESHelper.es_version_satisfies?(">= 5")
|
4
|
+
describe "Ingest pipeline execution behavior", :integration => true do
|
5
|
+
subject! do
|
6
|
+
require "logstash/outputs/elasticsearch"
|
7
|
+
settings = {
|
8
|
+
"hosts" => "#{get_host_port()}",
|
9
|
+
"pipeline" => "apache-logs"
|
10
|
+
}
|
11
|
+
next LogStash::Outputs::ElasticSearch.new(settings)
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:http_client) { Manticore::Client.new }
|
15
|
+
let(:ingest_url) { "http://#{get_host_port()}/_ingest/pipeline/apache-logs" }
|
16
|
+
let(:apache_logs_pipeline) { '
|
17
|
+
{
|
18
|
+
"description" : "Pipeline to parse Apache logs",
|
19
|
+
"processors" : [
|
20
|
+
{
|
21
|
+
"grok": {
|
22
|
+
"field": "message",
|
23
|
+
"patterns": ["%{COMBINEDAPACHELOG}"]
|
24
|
+
}
|
25
|
+
}
|
26
|
+
]
|
27
|
+
}'
|
28
|
+
}
|
29
|
+
|
30
|
+
before :each do
|
31
|
+
# Delete all templates first.
|
32
|
+
require "elasticsearch"
|
33
|
+
|
34
|
+
# Clean ES of data before we start.
|
35
|
+
@es = get_client
|
36
|
+
@es.indices.delete_template(:name => "*")
|
37
|
+
|
38
|
+
# This can fail if there are no indexes, ignore failure.
|
39
|
+
@es.indices.delete(:index => "*") rescue nil
|
40
|
+
|
41
|
+
# delete existing ingest pipeline
|
42
|
+
http_client.delete(ingest_url).call
|
43
|
+
|
44
|
+
# register pipeline
|
45
|
+
http_client.put(ingest_url, :body => apache_logs_pipeline, :headers => {"Content-Type" => "application/json" }).call
|
46
|
+
|
47
|
+
#TODO: Use esclient
|
48
|
+
#@es.ingest.put_pipeline :id => 'apache_pipeline', :body => pipeline_defintion
|
49
|
+
|
50
|
+
subject.register
|
51
|
+
subject.multi_receive([LogStash::Event.new("message" => '183.60.215.50 - - [01/Jun/2015:18:00:00 +0000] "GET /scripts/netcat-webserver HTTP/1.1" 200 182 "-" "Mozilla/5.0 (compatible; EasouSpider; +http://www.easou.com/search/spider.html)"')])
|
52
|
+
@es.indices.refresh
|
53
|
+
|
54
|
+
#Wait or fail until everything's indexed.
|
55
|
+
Stud::try(20.times) do
|
56
|
+
r = @es.search
|
57
|
+
expect(r).to have_hits(1)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
it "indexes using the proper pipeline" do
|
62
|
+
results = @es.search(:index => 'logstash-*', :q => "message:\"netcat\"")
|
63
|
+
expect(results).to have_hits(1)
|
64
|
+
expect(results["hits"]["hits"][0]["_source"]["response"]).to eq("200")
|
65
|
+
expect(results["hits"]["hits"][0]["_source"]["bytes"]).to eq("182")
|
66
|
+
expect(results["hits"]["hits"][0]["_source"]["verb"]).to eq("GET")
|
67
|
+
expect(results["hits"]["hits"][0]["_source"]["request"]).to eq("/scripts/netcat-webserver")
|
68
|
+
expect(results["hits"]["hits"][0]["_source"]["auth"]).to eq("-")
|
69
|
+
expect(results["hits"]["hits"][0]["_source"]["ident"]).to eq("-")
|
70
|
+
expect(results["hits"]["hits"][0]["_source"]["clientip"]).to eq("183.60.215.50")
|
71
|
+
expect(results["hits"]["hits"][0]["_source"]["junkfieldaaaa"]).to eq(nil)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require_relative "../../../spec/es_spec_helper"
|
2
|
+
|
3
|
+
describe "metrics", :integration => true do
|
4
|
+
subject! do
|
5
|
+
require "logstash/outputs/elasticsearch"
|
6
|
+
settings = {
|
7
|
+
"manage_template" => false,
|
8
|
+
"hosts" => "#{get_host_port()}"
|
9
|
+
}
|
10
|
+
plugin = LogStash::Outputs::ElasticSearch.new(settings)
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:metric) { subject.metric }
|
14
|
+
let(:bulk_request_metrics) { subject.instance_variable_get(:@bulk_request_metrics) }
|
15
|
+
let(:document_level_metrics) { subject.instance_variable_get(:@document_level_metrics) }
|
16
|
+
|
17
|
+
before :each do
|
18
|
+
require "elasticsearch"
|
19
|
+
|
20
|
+
# Clean ES of data before we start.
|
21
|
+
@es = get_client
|
22
|
+
@es.indices.delete_template(:name => "*")
|
23
|
+
|
24
|
+
# This can fail if there are no indexes, ignore failure.
|
25
|
+
@es.indices.delete(:index => "*") rescue nil
|
26
|
+
#@es.indices.refresh
|
27
|
+
subject.register
|
28
|
+
end
|
29
|
+
|
30
|
+
context "after a succesful bulk insert" do
|
31
|
+
let(:bulk) { [
|
32
|
+
LogStash::Event.new("message" => "sample message here"),
|
33
|
+
LogStash::Event.new("somemessage" => { "message" => "sample nested message here" }),
|
34
|
+
LogStash::Event.new("somevalue" => 100),
|
35
|
+
LogStash::Event.new("somevalue" => 10),
|
36
|
+
LogStash::Event.new("somevalue" => 1),
|
37
|
+
LogStash::Event.new("country" => "us"),
|
38
|
+
LogStash::Event.new("country" => "at"),
|
39
|
+
LogStash::Event.new("geoip" => { "location" => [ 0.0, 0.0 ] })
|
40
|
+
]}
|
41
|
+
|
42
|
+
it "increases successful bulk request metric" do
|
43
|
+
expect(bulk_request_metrics).to receive(:increment).with(:successes).once
|
44
|
+
subject.multi_receive(bulk)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "increases number of successful inserted documents" do
|
48
|
+
expect(document_level_metrics).to receive(:increment).with(:successes, bulk.size).once
|
49
|
+
subject.multi_receive(bulk)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "after a bulk insert that generates errors" do
|
54
|
+
let(:bulk) { [
|
55
|
+
LogStash::Event.new("message" => "sample message here"),
|
56
|
+
LogStash::Event.new("message" => { "message" => "sample nested message here" }),
|
57
|
+
]}
|
58
|
+
it "increases bulk request with error metric" do
|
59
|
+
expect(bulk_request_metrics).to receive(:increment).with(:with_errors).once
|
60
|
+
expect(bulk_request_metrics).to_not receive(:increment).with(:successes)
|
61
|
+
subject.multi_receive(bulk)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "increases number of successful and non retryable documents" do
|
65
|
+
expect(document_level_metrics).to receive(:increment).with(:non_retryable_failures).once
|
66
|
+
expect(document_level_metrics).to receive(:increment).with(:successes).once
|
67
|
+
subject.multi_receive(bulk)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "logstash/outputs/elasticsearch"
|
2
|
+
require_relative "../../../spec/es_spec_helper"
|
3
|
+
|
4
|
+
describe "elasticsearch is down on startup", :integration => true do
|
5
|
+
let(:event1) { LogStash::Event.new("somevalue" => 100, "@timestamp" => "2014-11-17T20:37:17.223Z", "@metadata" => {"retry_count" => 0}) }
|
6
|
+
let(:event2) { LogStash::Event.new("message" => "a") }
|
7
|
+
|
8
|
+
subject {
|
9
|
+
LogStash::Outputs::ElasticSearch.new({
|
10
|
+
"manage_template" => true,
|
11
|
+
"index" => "logstash-2014.11.17",
|
12
|
+
"template_overwrite" => true,
|
13
|
+
"hosts" => get_host_port(),
|
14
|
+
"retry_max_interval" => 64,
|
15
|
+
"retry_initial_interval" => 2
|
16
|
+
})
|
17
|
+
}
|
18
|
+
|
19
|
+
before :each do
|
20
|
+
# Delete all templates first.
|
21
|
+
require "elasticsearch"
|
22
|
+
allow(Stud).to receive(:stoppable_sleep)
|
23
|
+
|
24
|
+
# Clean ES of data before we start.
|
25
|
+
@es = get_client
|
26
|
+
@es.indices.delete_template(:name => "*")
|
27
|
+
@es.indices.delete(:index => "*")
|
28
|
+
@es.indices.refresh
|
29
|
+
end
|
30
|
+
|
31
|
+
after :each do
|
32
|
+
subject.close
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should ingest events when Elasticsearch recovers before documents are sent' do
|
36
|
+
allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_es_version).and_raise(::LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError.new(StandardError.new, "big fail"))
|
37
|
+
subject.register
|
38
|
+
allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_es_version).and_return(ESHelper.es_version)
|
39
|
+
subject.multi_receive([event1, event2])
|
40
|
+
@es.indices.refresh
|
41
|
+
r = @es.search
|
42
|
+
expect(r).to have_hits(2)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should ingest events when Elasticsearch recovers after documents are sent' do
|
46
|
+
allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_es_version).and_raise(::LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError.new(StandardError.new, "big fail"))
|
47
|
+
subject.register
|
48
|
+
Thread.new do
|
49
|
+
sleep 4
|
50
|
+
allow_any_instance_of(LogStash::Outputs::ElasticSearch::HttpClient::Pool).to receive(:get_es_version).and_return(ESHelper.es_version)
|
51
|
+
end
|
52
|
+
subject.multi_receive([event1, event2])
|
53
|
+
@es.indices.refresh
|
54
|
+
r = @es.search
|
55
|
+
expect(r).to have_hits(2)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require_relative "../../../spec/es_spec_helper"
|
2
|
+
|
3
|
+
if ESHelper.es_version_satisfies?(">= 5")
|
4
|
+
describe "Update actions using painless scripts", :integration => true, :update_tests => 'painless' do
|
5
|
+
require "logstash/outputs/elasticsearch"
|
6
|
+
|
7
|
+
def get_es_output( options={} )
|
8
|
+
settings = {
|
9
|
+
"manage_template" => true,
|
10
|
+
"index" => "logstash-update",
|
11
|
+
"template_overwrite" => true,
|
12
|
+
"hosts" => get_host_port(),
|
13
|
+
"action" => "update"
|
14
|
+
}
|
15
|
+
if ESHelper.es_version_satisfies?('<6')
|
16
|
+
settings.merge!({"script_lang" => "painless"})
|
17
|
+
end
|
18
|
+
LogStash::Outputs::ElasticSearch.new(settings.merge!(options))
|
19
|
+
end
|
20
|
+
|
21
|
+
before :each do
|
22
|
+
@es = get_client
|
23
|
+
# Delete all templates first.
|
24
|
+
# Clean ES of data before we start.
|
25
|
+
@es.indices.delete_template(:name => "*")
|
26
|
+
# This can fail if there are no indexes, ignore failure.
|
27
|
+
@es.indices.delete(:index => "*") rescue nil
|
28
|
+
@es.index(
|
29
|
+
:index => 'logstash-update',
|
30
|
+
:type => doc_type,
|
31
|
+
:id => "123",
|
32
|
+
:body => { :message => 'Test', :counter => 1 }
|
33
|
+
)
|
34
|
+
@es.indices.refresh
|
35
|
+
end
|
36
|
+
|
37
|
+
context "scripted updates" do
|
38
|
+
if ESHelper.es_version_satisfies?('<6')
|
39
|
+
context 'with file based scripts' do
|
40
|
+
it "should increment a counter with event/doc 'count' variable" do
|
41
|
+
subject = get_es_output({ 'document_id' => "123", 'script' => 'scripted_update', 'script_type' => 'file' })
|
42
|
+
subject.register
|
43
|
+
subject.multi_receive([LogStash::Event.new("count" => 2)])
|
44
|
+
r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
|
45
|
+
expect(r["_source"]["counter"]).to eq(3)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should increment a counter with event/doc '[data][count]' nested variable" do
|
49
|
+
subject = get_es_output({ 'document_id' => "123", 'script' => 'scripted_update_nested', 'script_type' => 'file' })
|
50
|
+
subject.register
|
51
|
+
subject.multi_receive([LogStash::Event.new("data" => { "count" => 3 })])
|
52
|
+
r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
|
53
|
+
expect(r["_source"]["counter"]).to eq(4)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should increment a counter with event/doc 'count' variable with inline script" do
|
59
|
+
subject = get_es_output({
|
60
|
+
'document_id' => "123",
|
61
|
+
'script' => 'ctx._source.counter += params.event.counter',
|
62
|
+
'script_type' => 'inline'
|
63
|
+
})
|
64
|
+
subject.register
|
65
|
+
subject.multi_receive([LogStash::Event.new("counter" => 3 )])
|
66
|
+
r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
|
67
|
+
expect(r["_source"]["counter"]).to eq(4)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should increment a counter with event/doc 'count' variable with event/doc as upsert and inline script" do
|
71
|
+
subject = get_es_output({
|
72
|
+
'document_id' => "123",
|
73
|
+
'doc_as_upsert' => true,
|
74
|
+
'script' => 'if( ctx._source.containsKey("counter") ){ ctx._source.counter += params.event.counter; } else { ctx._source.counter = params.event.counter; }',
|
75
|
+
'script_type' => 'inline'
|
76
|
+
})
|
77
|
+
subject.register
|
78
|
+
subject.multi_receive([LogStash::Event.new("counter" => 3 )])
|
79
|
+
r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
|
80
|
+
expect(r["_source"]["counter"]).to eq(4)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should, with new doc, set a counter with event/doc 'count' variable with event/doc as upsert and inline script" do
|
84
|
+
subject = get_es_output({
|
85
|
+
'document_id' => "456",
|
86
|
+
'doc_as_upsert' => true,
|
87
|
+
'script' => 'if( ctx._source.containsKey("counter") ){ ctx._source.counter += params.event.counter; } else { ctx._source.counter = params.event.counter; }',
|
88
|
+
'script_type' => 'inline'
|
89
|
+
})
|
90
|
+
subject.register
|
91
|
+
subject.multi_receive([LogStash::Event.new("counter" => 3 )])
|
92
|
+
r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
|
93
|
+
expect(r["_source"]["counter"]).to eq(3)
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'with an indexed script' do
|
97
|
+
it "should increment a counter with event/doc 'count' variable with indexed script" do
|
98
|
+
if ESHelper.es_version_satisfies?('<6')
|
99
|
+
@es.perform_request(:put, "_scripts/painless/indexed_update", {}, {"script" => "ctx._source.counter += params.event.count" })
|
100
|
+
else
|
101
|
+
@es.perform_request(:put, "_scripts/indexed_update", {}, {"script" => {"source" => "ctx._source.counter += params.event.count", "lang" => "painless"}})
|
102
|
+
end
|
103
|
+
|
104
|
+
plugin_parameters = {
|
105
|
+
'document_id' => "123",
|
106
|
+
'script' => 'indexed_update',
|
107
|
+
'script_type' => 'indexed'
|
108
|
+
}
|
109
|
+
|
110
|
+
if ESHelper.es_version_satisfies?('>= 6.0.0')
|
111
|
+
plugin_parameters.merge!('script_lang' => '')
|
112
|
+
end
|
113
|
+
|
114
|
+
subject = get_es_output(plugin_parameters)
|
115
|
+
subject.register
|
116
|
+
subject.multi_receive([LogStash::Event.new("count" => 4 )])
|
117
|
+
r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
|
118
|
+
expect(r["_source"]["counter"]).to eq(5)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "when update with upsert" do
|
124
|
+
it "should create new documents with provided upsert" do
|
125
|
+
subject = get_es_output({ 'document_id' => "456", 'upsert' => '{"message": "upsert message"}' })
|
126
|
+
subject.register
|
127
|
+
subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
|
128
|
+
r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
|
129
|
+
expect(r["_source"]["message"]).to eq('upsert message')
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should create new documents with event/doc as upsert" do
|
133
|
+
subject = get_es_output({ 'document_id' => "456", 'doc_as_upsert' => true })
|
134
|
+
subject.register
|
135
|
+
subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
|
136
|
+
r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
|
137
|
+
expect(r["_source"]["message"]).to eq('sample message here')
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should fail on documents with event/doc as upsert at external version" do
|
141
|
+
subject = get_es_output({ 'document_id' => "456", 'doc_as_upsert' => true, 'version' => 999, "version_type" => "external" })
|
142
|
+
expect { subject.register }.to raise_error(LogStash::ConfigurationError)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "updates with scripted upsert" do
|
147
|
+
if ESHelper.es_version_satisfies?('<6')
|
148
|
+
context 'with file based scripts' do
|
149
|
+
it "should create new documents with upsert content" do
|
150
|
+
subject = get_es_output({ 'document_id' => "456", 'script' => 'scripted_update', 'upsert' => '{"message": "upsert message"}', 'script_type' => 'file' })
|
151
|
+
subject.register
|
152
|
+
subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
|
153
|
+
r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
|
154
|
+
expect(r["_source"]["message"]).to eq('upsert message')
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should create new documents with event/doc as script params" do
|
158
|
+
subject = get_es_output({ 'document_id' => "456", 'script' => 'scripted_upsert', 'scripted_upsert' => true, 'script_type' => 'file' })
|
159
|
+
subject.register
|
160
|
+
subject.multi_receive([LogStash::Event.new("counter" => 1)])
|
161
|
+
@es.indices.refresh
|
162
|
+
r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
|
163
|
+
expect(r["_source"]["counter"]).to eq(1)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context 'with an inline script' do
|
169
|
+
it "should create new documents with upsert content" do
|
170
|
+
subject = get_es_output({ 'document_id' => "456", 'script' => 'ctx._source.counter = params.event.counter', 'upsert' => '{"message": "upsert message"}', 'script_type' => 'inline' })
|
171
|
+
subject.register
|
172
|
+
|
173
|
+
subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
|
174
|
+
r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
|
175
|
+
expect(r["_source"]["message"]).to eq('upsert message')
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should create new documents with event/doc as script params" do
|
179
|
+
subject = get_es_output({ 'document_id' => "456", 'script' => 'ctx._source.counter = params.event.counter', 'scripted_upsert' => true, 'script_type' => 'inline' })
|
180
|
+
subject.register
|
181
|
+
subject.multi_receive([LogStash::Event.new("counter" => 1)])
|
182
|
+
@es.indices.refresh
|
183
|
+
r = @es.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
|
184
|
+
expect(r["_source"]["counter"]).to eq(1)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|