logstash-output-opensearch 1.1.0-java → 2.0.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/COMPATIBILITY.md +5 -3
  4. data/CONTRIBUTING.md +26 -0
  5. data/DEVELOPER_GUIDE.md +9 -0
  6. data/Gemfile +1 -1
  7. data/MAINTAINERS.md +3 -0
  8. data/README.md +90 -3
  9. data/docs/ecs_compatibility.md +42 -0
  10. data/lib/logstash/outputs/opensearch/http_client/manticore_adapter.rb +122 -10
  11. data/lib/logstash/outputs/opensearch/http_client/pool.rb +11 -2
  12. data/lib/logstash/outputs/opensearch/http_client.rb +22 -25
  13. data/lib/logstash/outputs/opensearch/http_client_builder.rb +9 -3
  14. data/lib/logstash/outputs/opensearch/template_manager.rb +6 -5
  15. data/lib/logstash/outputs/opensearch/templates/ecs-disabled/1x_index.json +66 -0
  16. data/lib/logstash/outputs/opensearch/templates/ecs-disabled/2x.json +44 -0
  17. data/lib/logstash/outputs/opensearch/templates/ecs-disabled/2x_index.json +66 -0
  18. data/lib/logstash/outputs/opensearch/templates/ecs-disabled/7x_index.json +66 -0
  19. data/lib/logstash/outputs/opensearch/templates/ecs-v1/1x.json +3629 -0
  20. data/lib/logstash/outputs/opensearch/templates/ecs-v1/1x_index.json +3631 -0
  21. data/lib/logstash/outputs/opensearch/templates/ecs-v1/2x.json +3629 -0
  22. data/lib/logstash/outputs/opensearch/templates/ecs-v1/2x_index.json +3631 -0
  23. data/lib/logstash/outputs/opensearch/templates/ecs-v1/7x.json +3629 -0
  24. data/lib/logstash/outputs/opensearch/templates/ecs-v1/7x_index.json +3631 -0
  25. data/lib/logstash/outputs/opensearch/templates/ecs-v8/1x.json +5252 -0
  26. data/lib/logstash/outputs/opensearch/templates/ecs-v8/1x_index.json +5254 -0
  27. data/lib/logstash/outputs/opensearch/templates/ecs-v8/2x.json +5252 -0
  28. data/lib/logstash/outputs/opensearch/templates/ecs-v8/2x_index.json +5254 -0
  29. data/lib/logstash/outputs/opensearch/templates/ecs-v8/7x.json +5252 -0
  30. data/lib/logstash/outputs/opensearch/templates/ecs-v8/7x_index.json +5254 -0
  31. data/lib/logstash/outputs/opensearch.rb +10 -3
  32. data/lib/logstash/plugin_mixins/opensearch/api_configs.rb +26 -0
  33. data/logstash-output-opensearch.gemspec +19 -3
  34. data/spec/integration/outputs/compressed_indexing_spec.rb +10 -4
  35. data/spec/integration/outputs/create_spec.rb +7 -7
  36. data/spec/integration/outputs/delete_spec.rb +8 -8
  37. data/spec/integration/outputs/index_spec.rb +52 -10
  38. data/spec/integration/outputs/index_version_spec.rb +11 -11
  39. data/spec/integration/outputs/ingest_pipeline_spec.rb +2 -3
  40. data/spec/integration/outputs/metrics_spec.rb +0 -2
  41. data/spec/integration/outputs/no_opensearch_on_startup_spec.rb +0 -1
  42. data/spec/integration/outputs/painless_update_spec.rb +10 -10
  43. data/spec/integration/outputs/parent_spec.rb +1 -1
  44. data/spec/integration/outputs/retry_spec.rb +2 -2
  45. data/spec/integration/outputs/sniffer_spec.rb +2 -2
  46. data/spec/integration/outputs/templates_spec.rb +83 -59
  47. data/spec/integration/outputs/update_spec.rb +14 -14
  48. data/spec/opensearch_spec_helper.rb +12 -2
  49. data/spec/unit/outputs/opensearch/http_client/manticore_adapter_spec.rb +86 -3
  50. data/spec/unit/outputs/opensearch/http_client_spec.rb +26 -5
  51. data/spec/unit/outputs/opensearch/template_manager_spec.rb +11 -4
  52. data/spec/unit/outputs/opensearch_spec.rb +18 -2
  53. data.tar.gz.sig +0 -0
  54. metadata +74 -9
  55. metadata.gz.sig +0 -0
@@ -10,8 +10,12 @@
10
10
  require_relative "../../../spec/opensearch_spec_helper"
11
11
 
12
12
  describe "index template expected behavior", :integration => true do
13
+ let(:ecs_compatibility) { fail('spec group does not define `ecs_compatibility`!') }
14
+
13
15
  subject! do
14
16
  require "logstash/outputs/opensearch"
17
+ allow_any_instance_of(LogStash::Outputs::OpenSearch).to receive(:ecs_compatibility).and_return(ecs_compatibility)
18
+
15
19
  settings = {
16
20
  "manage_template" => true,
17
21
  "template_overwrite" => true,
@@ -21,8 +25,6 @@ describe "index template expected behavior", :integration => true do
21
25
  end
22
26
 
23
27
  before :each do
24
- # Delete all templates first.
25
- require "elasticsearch"
26
28
 
27
29
  # Clean OpenSearch of data before we start.
28
30
  @client = get_client
@@ -31,75 +33,97 @@ describe "index template expected behavior", :integration => true do
31
33
  # This can fail if there are no indexes, ignore failure.
32
34
  @client.indices.delete(:index => "*") rescue nil
33
35
 
34
- subject.register
35
-
36
- subject.multi_receive([
37
- LogStash::Event.new("message" => "sample message here"),
38
- LogStash::Event.new("somemessage" => { "message" => "sample nested message here" }),
39
- LogStash::Event.new("somevalue" => 100),
40
- LogStash::Event.new("somevalue" => 10),
41
- LogStash::Event.new("somevalue" => 1),
42
- LogStash::Event.new("country" => "us"),
43
- LogStash::Event.new("country" => "at"),
44
- LogStash::Event.new("geoip" => { "location" => [ 0.0, 0.0 ] })
45
- ])
46
-
47
- @client.indices.refresh
48
-
49
- # Wait or fail until everything's indexed.
50
- Stud::try(20.times) do
51
- r = @client.search(index: 'logstash-*')
52
- expect(r).to have_hits(8)
53
- end
54
36
  end
55
37
 
56
- it "permits phrase searching on string fields" do
57
- results = @client.search(:q => "message:\"sample message\"")
58
- expect(results).to have_hits(1)
59
- expect(results["hits"]["hits"][0]["_source"]["message"]).to eq("sample message here")
60
- end
38
+ context 'with ecs_compatibility => disabled' do
39
+ let(:ecs_compatibility) { :disabled }
40
+
41
+ before :each do
42
+ subject.register
43
+
44
+ subject.multi_receive([
45
+ LogStash::Event.new("message" => "sample message here"),
46
+ LogStash::Event.new("somemessage" => { "message" => "sample nested message here" }),
47
+ LogStash::Event.new("somevalue" => 100),
48
+ LogStash::Event.new("somevalue" => 10),
49
+ LogStash::Event.new("somevalue" => 1),
50
+ LogStash::Event.new("country" => "us"),
51
+ LogStash::Event.new("country" => "at"),
52
+ LogStash::Event.new("geoip" => { "location" => [ 0.0, 0.0 ] })
53
+ ])
54
+
55
+ @client.indices.refresh
56
+
57
+ # Wait or fail until everything's indexed.
58
+ Stud::try(20.times) do
59
+ r = @client.search(index: 'logstash-*')
60
+ expect(r).to have_hits(8)
61
+ end
62
+ end
61
63
 
62
- it "numbers dynamically map to a numeric type and permit range queries" do
63
- results = @client.search(:q => "somevalue:[5 TO 105]")
64
- expect(results).to have_hits(2)
64
+ it "permits phrase searching on string fields" do
65
+ results = @client.search(:q => "message:\"sample message\"")
66
+ expect(results).to have_hits(1)
67
+ expect(results["hits"]["hits"][0]["_source"]["message"]).to eq("sample message here")
68
+ end
65
69
 
66
- values = results["hits"]["hits"].collect { |r| r["_source"]["somevalue"] }
67
- expect(values).to include(10)
68
- expect(values).to include(100)
69
- expect(values).to_not include(1)
70
- end
70
+ it "numbers dynamically map to a numeric type and permit range queries" do
71
+ results = @client.search(:q => "somevalue:[5 TO 105]")
72
+ expect(results).to have_hits(2)
71
73
 
72
- it "does not create .keyword field for top-level message field" do
73
- results = @client.search(:q => "message.keyword:\"sample message here\"")
74
- expect(results).to have_hits(0)
75
- end
74
+ values = results["hits"]["hits"].collect { |r| r["_source"]["somevalue"] }
75
+ expect(values).to include(10)
76
+ expect(values).to include(100)
77
+ expect(values).to_not include(1)
78
+ end
76
79
 
77
- it "creates .keyword field for nested message fields" do
78
- results = @client.search(:q => "somemessage.message.keyword:\"sample nested message here\"")
79
- expect(results).to have_hits(1)
80
- end
80
+ it "does not create .keyword field for top-level message field" do
81
+ results = @client.search(:q => "message.keyword:\"sample message here\"")
82
+ expect(results).to have_hits(0)
83
+ end
81
84
 
82
- it "creates .keyword field from any string field which is not_analyzed" do
83
- results = @client.search(:q => "country.keyword:\"us\"")
84
- expect(results).to have_hits(1)
85
- expect(results["hits"]["hits"][0]["_source"]["country"]).to eq("us")
85
+ it "creates .keyword field for nested message fields" do
86
+ results = @client.search(:q => "somemessage.message.keyword:\"sample nested message here\"")
87
+ expect(results).to have_hits(1)
88
+ end
86
89
 
87
- # partial or terms should not work.
88
- results = @client.search(:q => "country.keyword:\"u\"")
89
- expect(results).to have_hits(0)
90
- end
90
+ it "creates .keyword field from any string field which is not_analyzed" do
91
+ results = @client.search(:q => "country.keyword:\"us\"")
92
+ expect(results).to have_hits(1)
93
+ expect(results["hits"]["hits"][0]["_source"]["country"]).to eq("us")
94
+
95
+ # partial or terms should not work.
96
+ results = @client.search(:q => "country.keyword:\"u\"")
97
+ expect(results).to have_hits(0)
98
+ end
99
+
100
+ it "make [geoip][location] a geo_point" do
101
+ expect(field_properties_from_template("logstash", "geoip")["location"]["type"]).to eq("geo_point")
102
+ end
91
103
 
92
- it "make [geoip][location] a geo_point" do
93
- expect(field_properties_from_template("logstash", "geoip")["location"]["type"]).to eq("geo_point")
104
+ it "aggregate .keyword results correctly " do
105
+ results = @client.search(:body => { "aggregations" => { "my_agg" => { "terms" => { "field" => "country.keyword" } } } })["aggregations"]["my_agg"]
106
+ terms = results["buckets"].collect { |b| b["key"] }
107
+
108
+ expect(terms).to include("us")
109
+
110
+ # 'at' is a stopword, make sure stopwords are not ignored.
111
+ expect(terms).to include("at")
112
+ end
94
113
  end
95
114
 
96
- it "aggregate .keyword results correctly " do
97
- results = @client.search(:body => { "aggregations" => { "my_agg" => { "terms" => { "field" => "country.keyword" } } } })["aggregations"]["my_agg"]
98
- terms = results["buckets"].collect { |b| b["key"] }
115
+ context 'with ECS enabled' do
116
+ let(:ecs_compatibility) { :v1 }
99
117
 
100
- expect(terms).to include("us")
118
+ before(:each) do
119
+ subject.register # should load template?
120
+ subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
121
+ end
101
122
 
102
- # 'at' is a stopword, make sure stopwords are not ignored.
103
- expect(terms).to include("at")
123
+ it 'loads the templates' do
124
+ aggregate_failures do
125
+ expect(@client.indices.exists_template(name: 'ecs-logstash')).to be_truthy
126
+ end
127
+ end
104
128
  end
105
129
  end
@@ -12,7 +12,7 @@ require_relative "../../../spec/opensearch_spec_helper"
12
12
  describe "Update actions without scripts", :integration => true do
13
13
  require "logstash/outputs/opensearch"
14
14
 
15
- def get_es_output( options={} )
15
+ def get_output( options={} )
16
16
  settings = {
17
17
  "manage_template" => true,
18
18
  "index" => "logstash-update",
@@ -40,20 +40,20 @@ describe "Update actions without scripts", :integration => true do
40
40
  end
41
41
 
42
42
  it "should fail without a document_id" do
43
- subject = get_es_output
43
+ subject = get_output
44
44
  expect { subject.register }.to raise_error(LogStash::ConfigurationError)
45
45
  end
46
46
 
47
47
  context "when update only" do
48
48
  it "should not create new document" do
49
- subject = get_es_output({ 'document_id' => "456" } )
49
+ subject = get_output({ 'document_id' => "456" } )
50
50
  subject.register
51
51
  subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
52
- expect {@client.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)}.to raise_error(Elasticsearch::Transport::Transport::Errors::NotFound)
52
+ expect {@client.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)}.to raise_error(OpenSearch::Transport::Transport::Errors::NotFound)
53
53
  end
54
54
 
55
55
  it "should update existing document" do
56
- subject = get_es_output({ 'document_id' => "123" })
56
+ subject = get_output({ 'document_id' => "123" })
57
57
  subject.register
58
58
  subject.multi_receive([LogStash::Event.new("message" => "updated message here")])
59
59
  r = @client.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
@@ -63,7 +63,7 @@ describe "Update actions without scripts", :integration => true do
63
63
  # The es ruby client treats the data field differently. Make sure this doesn't
64
64
  # raise an exception
65
65
  it "should update an existing document that has a 'data' field" do
66
- subject = get_es_output({ 'document_id' => "123" })
66
+ subject = get_output({ 'document_id' => "123" })
67
67
  subject.register
68
68
  subject.multi_receive([LogStash::Event.new("data" => "updated message here", "message" => "foo")])
69
69
  r = @client.get(:index => 'logstash-update', :type => doc_type, :id => "123", :refresh => true)
@@ -72,27 +72,27 @@ describe "Update actions without scripts", :integration => true do
72
72
  end
73
73
 
74
74
  it "should allow default (internal) version" do
75
- subject = get_es_output({ 'document_id' => "123", "version" => "99" })
75
+ subject = get_output({ 'document_id' => "123", "version" => "99" })
76
76
  subject.register
77
77
  end
78
78
 
79
79
  it "should allow internal version" do
80
- subject = get_es_output({ 'document_id' => "123", "version" => "99", "version_type" => "internal" })
80
+ subject = get_output({ 'document_id' => "123", "version" => "99", "version_type" => "internal" })
81
81
  subject.register
82
82
  end
83
83
 
84
84
  it "should not allow external version" do
85
- subject = get_es_output({ 'document_id' => "123", "version" => "99", "version_type" => "external" })
85
+ subject = get_output({ 'document_id' => "123", "version" => "99", "version_type" => "external" })
86
86
  expect { subject.register }.to raise_error(LogStash::ConfigurationError)
87
87
  end
88
88
 
89
89
  it "should not allow external_gt version" do
90
- subject = get_es_output({ 'document_id' => "123", "version" => "99", "version_type" => "external_gt" })
90
+ subject = get_output({ 'document_id' => "123", "version" => "99", "version_type" => "external_gt" })
91
91
  expect { subject.register }.to raise_error(LogStash::ConfigurationError)
92
92
  end
93
93
 
94
94
  it "should not allow external_gte version" do
95
- subject = get_es_output({ 'document_id' => "123", "version" => "99", "version_type" => "external_gte" })
95
+ subject = get_output({ 'document_id' => "123", "version" => "99", "version_type" => "external_gte" })
96
96
  expect { subject.register }.to raise_error(LogStash::ConfigurationError)
97
97
  end
98
98
 
@@ -100,7 +100,7 @@ describe "Update actions without scripts", :integration => true do
100
100
 
101
101
  context "when update with upsert" do
102
102
  it "should create new documents with provided upsert" do
103
- subject = get_es_output({ 'document_id' => "456", 'upsert' => '{"message": "upsert message"}' })
103
+ subject = get_output({ 'document_id' => "456", 'upsert' => '{"message": "upsert message"}' })
104
104
  subject.register
105
105
  subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
106
106
  r = @client.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
@@ -108,7 +108,7 @@ describe "Update actions without scripts", :integration => true do
108
108
  end
109
109
 
110
110
  it "should create new documents with event/doc as upsert" do
111
- subject = get_es_output({ 'document_id' => "456", 'doc_as_upsert' => true })
111
+ subject = get_output({ 'document_id' => "456", 'doc_as_upsert' => true })
112
112
  subject.register
113
113
  subject.multi_receive([LogStash::Event.new("message" => "sample message here")])
114
114
  r = @client.get(:index => 'logstash-update', :type => doc_type, :id => "456", :refresh => true)
@@ -116,7 +116,7 @@ describe "Update actions without scripts", :integration => true do
116
116
  end
117
117
 
118
118
  it "should fail on documents with event/doc as upsert at external version" do
119
- subject = get_es_output({ 'document_id' => "456", 'doc_as_upsert' => true, 'version' => 999, "version_type" => "external" })
119
+ subject = get_output({ 'document_id' => "456", 'doc_as_upsert' => true, 'version' => 999, "version_type" => "external" })
120
120
  expect { subject.register }.to raise_error(LogStash::ConfigurationError)
121
121
  end
122
122
  end
@@ -9,7 +9,7 @@
9
9
 
10
10
  require_relative './spec_helper'
11
11
 
12
- require 'elasticsearch'
12
+ require 'opensearch'
13
13
 
14
14
  require 'json'
15
15
  require 'cabin'
@@ -24,7 +24,7 @@ module OpenSearchHelper
24
24
  end
25
25
 
26
26
  def get_client
27
- Elasticsearch::Client.new(:hosts => [get_host_port])
27
+ OpenSearch::Client.new(:hosts => [get_host_port])
28
28
  end
29
29
 
30
30
  def doc_type
@@ -49,6 +49,16 @@ module OpenSearchHelper
49
49
  RSpec.configuration.filter[:version]
50
50
  end
51
51
 
52
+ def self.check_version?(*requirement)
53
+ version = self.version
54
+ if version.nil? || version.empty?
55
+ puts "version tag isn't set. Returning false from 'check_version?`."
56
+ return false
57
+ end
58
+ release_version = Gem::Version.new(version).release
59
+ Gem::Requirement.new(requirement).satisfied_by?(release_version)
60
+ end
61
+
52
62
  RSpec::Matchers.define :have_hits do |expected|
53
63
  match do |actual|
54
64
  expected == actual['hits']['total']['value']
@@ -25,13 +25,13 @@ describe LogStash::Outputs::OpenSearch::HttpClient::ManticoreAdapter do
25
25
  it "should implement host unreachable exceptions" do
26
26
  expect(subject.host_unreachable_exceptions).to be_a(Array)
27
27
  end
28
-
28
+
29
29
  describe "auth" do
30
30
  let(:user) { "myuser" }
31
31
  let(:password) { "mypassword" }
32
32
  let(:noauth_uri) { clone = uri.clone; clone.user=nil; clone.password=nil; clone }
33
33
  let(:uri) { ::LogStash::Util::SafeURI.new("http://#{user}:#{password}@localhost:9200") }
34
-
34
+
35
35
  it "should convert the auth to params" do
36
36
  resp = double("response")
37
37
  allow(resp).to receive(:call)
@@ -39,7 +39,7 @@ describe LogStash::Outputs::OpenSearch::HttpClient::ManticoreAdapter do
39
39
 
40
40
  expected_uri = noauth_uri.clone
41
41
  expected_uri.path = "/"
42
-
42
+
43
43
  expect(subject.manticore).to receive(:get).
44
44
  with(expected_uri.to_s, {
45
45
  :headers => {"content-type" => "application/json"},
@@ -54,6 +54,89 @@ describe LogStash::Outputs::OpenSearch::HttpClient::ManticoreAdapter do
54
54
  end
55
55
  end
56
56
 
57
+ describe "aws_iam" do
58
+ let(:options) { {
59
+ :auth_type => {
60
+ "type"=>"aws_iam",
61
+ "aws_access_key_id"=>"AAAAAAAAAAAAAAAAAAAA",
62
+ "aws_secret_access_key"=>"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"}
63
+ } }
64
+ let(:options_svc) { {
65
+ :auth_type => {
66
+ "type"=>"aws_iam",
67
+ "aws_access_key_id"=>"AAAAAAAAAAAAAAAAAAAA",
68
+ "aws_secret_access_key"=>"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
69
+ "service_name"=>"svc_test"}
70
+ } }
71
+ subject { described_class.new(logger, options) }
72
+ let(:uri) { ::LogStash::Util::SafeURI.new("http://localhost:9200") }
73
+ let(:sign_aws_request) { }
74
+
75
+ it "should validate AWS IAM credentials initialization" do
76
+ expect(subject.aws_iam_auth_initialization(options)).not_to be_nil
77
+ expect(subject.get_service_name).to eq("es")
78
+ end
79
+
80
+ it "should validate AWS IAM service_name config" do
81
+ expect(subject.aws_iam_auth_initialization(options_svc)).not_to be_nil
82
+ expect(subject.get_service_name).to eq("svc_test")
83
+ end
84
+
85
+ it "should validate signing aws request" do
86
+ resp = double("response")
87
+ allow(resp).to receive(:call)
88
+ allow(resp).to receive(:code).and_return(200)
89
+ allow(subject).to receive(:sign_aws_request).with(any_args).and_return(sign_aws_request)
90
+
91
+ expected_uri = uri.clone
92
+ expected_uri.path = "/"
93
+
94
+ expect(subject.manticore).to receive(:get).
95
+ with(expected_uri.to_s, {
96
+ :headers => {"content-type"=> "application/json"}
97
+ }
98
+ ).and_return resp
99
+
100
+ expect(subject).to receive(:sign_aws_request)
101
+ subject.perform_request(uri, :get, "/")
102
+ end
103
+ end
104
+
105
+ describe "basic_auth" do
106
+ let(:options) { {
107
+ :auth_type => {
108
+ "type"=>"basic",
109
+ "user" => "myuser",
110
+ "password" => "mypassword"}
111
+ } }
112
+ subject { described_class.new(logger, options) }
113
+ let(:user) {options[:auth_type]["user"]}
114
+ let(:password) {options[:auth_type]["password"]}
115
+ let(:noauth_uri) { clone = uri.clone; clone.user=nil; clone.password=nil; clone }
116
+ let(:uri) { ::LogStash::Util::SafeURI.new("http://localhost:9200") }
117
+
118
+ it "should validate master credentials with type as 'basic_auth'" do
119
+ resp = double("response")
120
+ allow(resp).to receive(:call)
121
+ allow(resp).to receive(:code).and_return(200)
122
+
123
+ expected_uri = noauth_uri.clone
124
+ expected_uri.path = "/"
125
+
126
+ expect(subject.manticore).to receive(:get).
127
+ with(expected_uri.to_s, {
128
+ :headers => {"content-type" => "application/json"},
129
+ :auth => {
130
+ :user => user,
131
+ :password => password,
132
+ :eager => true
133
+ }
134
+ }).and_return resp
135
+
136
+ subject.perform_request(uri, :get, "/")
137
+ end
138
+ end
139
+
57
140
  describe "bad response codes" do
58
141
  let(:uri) { ::LogStash::Util::SafeURI.new("http://localhost:9200") }
59
142
 
@@ -17,6 +17,7 @@ describe LogStash::Outputs::OpenSearch::HttpClient do
17
17
  opts = {
18
18
  :hosts => [::LogStash::Util::SafeURI.new("127.0.0.1")],
19
19
  :logger => Cabin::Channel.get,
20
+ :target_bulk_bytes => 9_000_000,
20
21
  :metric => ::LogStash::Instrument::NullMetric.new(:dummy).namespace(:alsodummy)
21
22
  }
22
23
 
@@ -156,11 +157,31 @@ describe LogStash::Outputs::OpenSearch::HttpClient do
156
157
  }
157
158
 
158
159
  it "should call index template" do
159
- expect(subject.pool).to receive(:put).with("_template/#{template_name}", nil, anything).and_return(get_response)
160
+ expect(subject.pool).to receive(:put).with("/_template/#{template_name}", nil, anything).and_return(get_response)
160
161
  subject.template_put(template_name, template)
161
162
  end
162
163
  end
163
164
 
165
+ describe "legacy_template" do
166
+ let(:template_name) { "logstash" }
167
+ let(:template) { {} }
168
+ let(:get_response) {
169
+ double("response", :body => {}, :code => 404)
170
+ }
171
+ [true, false].each do |legacy_template|
172
+ context "when legacy_template => #{legacy_template}" do
173
+ let(:base_options) { super().merge(:client_settings => {:legacy_template => legacy_template}) }
174
+ subject { described_class.new(base_options) }
175
+ endpoint = legacy_template ? "_template" : "_index_template"
176
+ it "should call template_endpoint #{endpoint}" do
177
+ expect(subject.pool).to receive(:head).with("/#{endpoint}/#{template_name}").and_return(get_response)
178
+ expect(subject.pool).to receive(:put).with("/#{endpoint}/#{template_name}", nil, anything).and_return(get_response)
179
+ subject.template_install(template_name, template)
180
+ end
181
+ end
182
+ end
183
+ end
184
+
164
185
  describe "join_bulk_responses" do
165
186
  subject { described_class.new(base_options) }
166
187
 
@@ -226,8 +247,8 @@ describe LogStash::Outputs::OpenSearch::HttpClient do
226
247
  end
227
248
  end
228
249
 
229
- context "if a message is over TARGET_BULK_BYTES" do
230
- let(:target_bulk_bytes) { LogStash::Outputs::OpenSearch::TARGET_BULK_BYTES }
250
+ context "if a message is over target_bulk_bytes" do
251
+ let(:target_bulk_bytes) { subject.target_bulk_bytes }
231
252
  let(:message) { "a" * (target_bulk_bytes + 1) }
232
253
 
233
254
  it "should be handled properly" do
@@ -256,8 +277,8 @@ describe LogStash::Outputs::OpenSearch::HttpClient do
256
277
  s = subject.send(:bulk, actions)
257
278
  end
258
279
 
259
- context "if one exceeds TARGET_BULK_BYTES" do
260
- let(:target_bulk_bytes) { LogStash::Outputs::OpenSearch::TARGET_BULK_BYTES }
280
+ context "if one exceeds target_bulk_bytes" do
281
+ let(:target_bulk_bytes) { subject.target_bulk_bytes }
261
282
  let(:message1) { "a" * (target_bulk_bytes + 1) }
262
283
  it "executes two bulk_send operations" do
263
284
  allow(subject).to receive(:join_bulk_responses)
@@ -13,9 +13,16 @@ require "logstash/outputs/opensearch/template_manager"
13
13
  describe LogStash::Outputs::OpenSearch::TemplateManager do
14
14
 
15
15
  describe ".default_template_path" do
16
- context 'when ECS v1 is requested' do
17
- it 'resolves' do
18
- expect(described_class.default_template_path(7, :v1)).to end_with("/templates/ecs-v1/7x.json")
16
+ [7, 1, 2].each do |major_version|
17
+ [:disabled, :v1, :v8].each do |ecs_ver|
18
+ [true, false].each do |legacy_template|
19
+ context "when ECS is #{ecs_ver} with OpenSearch #{major_version}.x legacy_template:#{legacy_template}" do
20
+ suffix = legacy_template ? "" : "_index"
21
+ it 'resolves' do
22
+ expect(described_class.default_template_path(major_version, ecs_ver, legacy_template)).to end_with("/templates/ecs-#{ecs_ver}/#{major_version}x#{suffix}.json")
23
+ end
24
+ end
25
+ end
19
26
  end
20
27
  end
21
28
  end
@@ -33,4 +40,4 @@ describe LogStash::Outputs::OpenSearch::TemplateManager do
33
40
  end
34
41
  end
35
42
  end
36
- end
43
+ end
@@ -12,6 +12,7 @@ require "base64"
12
12
  require "flores/random"
13
13
  require 'concurrent/atomic/count_down_latch'
14
14
  require "logstash/outputs/opensearch"
15
+ require 'logstash/plugin_mixins/ecs_compatibility_support/spec_helper'
15
16
 
16
17
  describe LogStash::Outputs::OpenSearch do
17
18
  subject(:opensearch_output_instance) { described_class.new(options) }
@@ -325,7 +326,7 @@ describe LogStash::Outputs::OpenSearch do
325
326
  end
326
327
 
327
328
  context '413 errors' do
328
- let(:payload_size) { LogStash::Outputs::OpenSearch::TARGET_BULK_BYTES + 1024 }
329
+ let(:payload_size) { subject.client.target_bulk_bytes + 1024 }
329
330
  let(:event) { ::LogStash::Event.new("message" => ("a" * payload_size ) ) }
330
331
 
331
332
  let(:logger_stub) { double("logger").as_null_object }
@@ -357,7 +358,7 @@ describe LogStash::Outputs::OpenSearch do
357
358
 
358
359
  expect(logger_stub).to have_received(:warn)
359
360
  .with(a_string_matching(/413 Payload Too Large/),
360
- hash_including(:action_count => 1, :content_length => a_value > 20_000_000))
361
+ hash_including(:action_count => 1, :content_length => a_value > subject.client.target_bulk_bytes))
361
362
  end
362
363
  end
363
364
 
@@ -784,6 +785,21 @@ describe LogStash::Outputs::OpenSearch do
784
785
  end
785
786
  end
786
787
 
788
+ describe 'ecs_compatibility support', :ecs_compatibility_support do
789
+ [:disabled, :v1, :v8].each do |ecs_compatibility|
790
+ context "when `ecs_compatibility => #{ecs_compatibility}`" do
791
+ let(:options) { Hash.new }
792
+ subject(:output) { described_class.new(options.merge("ecs_compatibility" => "#{ecs_compatibility}")) }
793
+ context 'when registered' do
794
+ before(:each) { output.register }
795
+ it 'has the correct effective ECS compatibility setting' do
796
+ expect(output.ecs_compatibility).to eq(ecs_compatibility)
797
+ end
798
+ end
799
+ end
800
+ end
801
+ end
802
+
787
803
  @private
788
804
 
789
805
  def stub_manticore_client!(manticore_double = nil)
data.tar.gz.sig CHANGED
Binary file