logstash-output-opensearch 1.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/ADMINS.md +29 -0
  5. data/CODE_OF_CONDUCT.md +25 -0
  6. data/CONTRIBUTING.md +99 -0
  7. data/DEVELOPER_GUIDE.md +208 -0
  8. data/Gemfile +20 -0
  9. data/LICENSE +202 -0
  10. data/MAINTAINERS.md +71 -0
  11. data/NOTICE +2 -0
  12. data/README.md +37 -0
  13. data/RELEASING.md +36 -0
  14. data/SECURITY.md +3 -0
  15. data/lib/logstash/outputs/opensearch.rb +449 -0
  16. data/lib/logstash/outputs/opensearch/distribution_checker.rb +44 -0
  17. data/lib/logstash/outputs/opensearch/http_client.rb +465 -0
  18. data/lib/logstash/outputs/opensearch/http_client/manticore_adapter.rb +140 -0
  19. data/lib/logstash/outputs/opensearch/http_client/pool.rb +467 -0
  20. data/lib/logstash/outputs/opensearch/http_client_builder.rb +182 -0
  21. data/lib/logstash/outputs/opensearch/template_manager.rb +60 -0
  22. data/lib/logstash/outputs/opensearch/templates/ecs-disabled/1x.json +44 -0
  23. data/lib/logstash/outputs/opensearch/templates/ecs-disabled/7x.json +44 -0
  24. data/lib/logstash/plugin_mixins/opensearch/api_configs.rb +168 -0
  25. data/lib/logstash/plugin_mixins/opensearch/common.rb +294 -0
  26. data/lib/logstash/plugin_mixins/opensearch/noop_distribution_checker.rb +18 -0
  27. data/logstash-output-opensearch.gemspec +40 -0
  28. data/spec/fixtures/_nodes/nodes.json +74 -0
  29. data/spec/fixtures/htpasswd +2 -0
  30. data/spec/fixtures/nginx_reverse_proxy.conf +22 -0
  31. data/spec/fixtures/scripts/painless/scripted_update.painless +2 -0
  32. data/spec/fixtures/scripts/painless/scripted_update_nested.painless +1 -0
  33. data/spec/fixtures/scripts/painless/scripted_upsert.painless +1 -0
  34. data/spec/integration/outputs/compressed_indexing_spec.rb +76 -0
  35. data/spec/integration/outputs/create_spec.rb +76 -0
  36. data/spec/integration/outputs/delete_spec.rb +72 -0
  37. data/spec/integration/outputs/index_spec.rb +164 -0
  38. data/spec/integration/outputs/index_version_spec.rb +110 -0
  39. data/spec/integration/outputs/ingest_pipeline_spec.rb +82 -0
  40. data/spec/integration/outputs/metrics_spec.rb +75 -0
  41. data/spec/integration/outputs/no_opensearch_on_startup_spec.rb +67 -0
  42. data/spec/integration/outputs/painless_update_spec.rb +147 -0
  43. data/spec/integration/outputs/parent_spec.rb +103 -0
  44. data/spec/integration/outputs/retry_spec.rb +182 -0
  45. data/spec/integration/outputs/routing_spec.rb +70 -0
  46. data/spec/integration/outputs/sniffer_spec.rb +70 -0
  47. data/spec/integration/outputs/templates_spec.rb +105 -0
  48. data/spec/integration/outputs/update_spec.rb +123 -0
  49. data/spec/opensearch_spec_helper.rb +141 -0
  50. data/spec/spec_helper.rb +19 -0
  51. data/spec/unit/http_client_builder_spec.rb +194 -0
  52. data/spec/unit/outputs/error_whitelist_spec.rb +62 -0
  53. data/spec/unit/outputs/opensearch/http_client/manticore_adapter_spec.rb +159 -0
  54. data/spec/unit/outputs/opensearch/http_client/pool_spec.rb +306 -0
  55. data/spec/unit/outputs/opensearch/http_client_spec.rb +292 -0
  56. data/spec/unit/outputs/opensearch/template_manager_spec.rb +36 -0
  57. data/spec/unit/outputs/opensearch_proxy_spec.rb +112 -0
  58. data/spec/unit/outputs/opensearch_spec.rb +800 -0
  59. data/spec/unit/outputs/opensearch_ssl_spec.rb +179 -0
  60. metadata +289 -0
  61. metadata.gz.sig +0 -0
data/MAINTAINERS.md ADDED
@@ -0,0 +1,71 @@
1
+ - [Overview](#overview)
2
+ - [Current Maintainers](#current-maintainers)
3
+ - [Maintainer Responsibilities](#maintainer-responsibilities)
4
+ - [Uphold Code of Conduct](#uphold-code-of-conduct)
5
+ - [Prioritize Security](#prioritize-security)
6
+ - [Review Pull Requests](#review-pull-requests)
7
+ - [Triage Open Issues](#triage-open-issues)
8
+ - [Be Responsive](#be-responsive)
9
+ - [Maintain Overall Health of the Repo](#maintain-overall-health-of-the-repo)
10
+ - [Use Semver](#use-semver)
11
+ - [Release Frequently](#release-frequently)
12
+ - [Promote Other Maintainers](#promote-other-maintainers)
13
+
14
+ ## Overview
15
+
16
+ This document explains who the maintainers are (see below), what they do in this repo, and how they should be doing it. If you're interested in contributing, see [CONTRIBUTING](CONTRIBUTING.md).
17
+
18
+ ## Current Maintainers
19
+
20
+ | Maintainer | GitHub ID | Affiliation |
21
+ | ------------------------ | --------------------------------------- | ----------- |
22
+ | Jack Mazanec | [jmazanec15](https://github.com/jmazanec15) | Amazon |
23
+ | Vamshi Vijay Nakkirtha | [vamshin](https://github.com/vamshin) | Amazon |
24
+ | Vijayan Balasubramanian | [VijayanB](https://github.com/VijayanB) | Amazon |
25
+
26
+ ## Maintainer Responsibilities
27
+
28
+ Maintainers are active and visible members of the community, and have [maintain-level permissions on a repository](https://docs.github.com/en/organizations/managing-access-to-your-organizations-repositories/repository-permission-levels-for-an-organization). Use those privileges to serve the community and evolve code as follows.
29
+
30
+ ### Uphold Code of Conduct
31
+
32
+ Model the behavior set forward by the [Code of Conduct](CODE_OF_CONDUCT.md) and raise any violations to other maintainers and admins.
33
+
34
+ ### Prioritize Security
35
+
36
+ Security is your number one priority. Maintainer's Github keys must be password protected securely and any reported security vulnerabilities are addressed before features or bugs.
37
+
38
+ Note that this repository is monitored and supported 24/7 by Amazon Security, see [Reporting a Vulnerability](SECURITY.md) for details.
39
+
40
+ ### Review Pull Requests
41
+
42
+ Review pull requests regularly, comment, suggest, reject, merge and close. Accept only high quality pull-requests. Provide code reviews and guidance on incomming pull requests. Don't let PRs be stale and do your best to be helpful to contributors.
43
+
44
+ ### Triage Open Issues
45
+
46
+ Manage labels, review issues regularly, and triage by labelling them.
47
+
48
+ All repositories in this organization have a standard set of labels, including `bug`, `documentation`, `duplicate`, `enhancement`, `good first issue`, `help wanted`, `blocker`, `invalid`, `question`, `wontfix`, and `untriaged`, along with release labels, such as `v1.0.0`, `v1.1.0` and `v2.0.0`, and `backport`.
49
+
50
+ Use labels to target an issue or a PR for a given release, add `help wanted` to good issues for new community members, and `blocker` for issues that scare you or need immediate attention. Request for more information from a submitter if an issue is not clear. Create new labels as needed by the project.
51
+
52
+ ### Be Responsive
53
+
54
+ Respond to enhancement requests, and forum posts. Allocate time to reviewing and commenting on issues and conversations as they come in.
55
+
56
+ ### Maintain Overall Health of the Repo
57
+
58
+ Keep the `main` branch at production quality at all times. Backport features as needed. Cut release branches and tags to enable future patches.
59
+
60
+ ### Use Semver
61
+
62
+ Use and enforce [semantic versioning](https://semver.org/) and do not let breaking changes be made outside of major releases.
63
+
64
+ ### Release Frequently
65
+
66
+ Make frequent project releases to the community.
67
+
68
+ ### Promote Other Maintainers
69
+
70
+ Assist, add, and remove [MAINTAINERS](MAINTAINERS.md). Exercise good judgement, and propose high quality contributors to become co-maintainers.
71
+
data/NOTICE ADDED
@@ -0,0 +1,2 @@
1
+ logstash-output-opensearch
2
+ Copyright 2021 logstash-output-opensearch Contributors
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ [![Build and Test logstash-output-opensearch plugin](https://github.com/opensearch-project/logstash-output-opensearch/actions/workflows/CI.yml/badge.svg)](https://github.com/opensearch-project/logstash-output-opensearch/actions/workflows/CI.yml)
2
+ ![PRs welcome!](https://img.shields.io/badge/PRs-welcome!-success)
3
+ # Logstash Plugin
4
+
5
+ - [Welcome!](#welcome)
6
+ - [Project Resources](#project-resources)
7
+ - [Code of Conduct](#code-of-conduct)
8
+ - [License](#license)
9
+ - [Copyright](#copyright)
10
+
11
+ ## Welcome!
12
+
13
+ **logstash-output-opensearch** is a community-driven, open source fork logstash-output-elasticsearch licensed under the [Apache v2.0 License](LICENSE.txt). For more information, see [opensearch.org](https://opensearch.org/).
14
+
15
+ ## Project Resources
16
+
17
+ * [Project Website](https://opensearch.org/)
18
+ * [Documentation](https://opensearch.org/)
19
+ * Need help? Try [Forums](https://discuss.opendistrocommunity.dev/)
20
+ * [Project Principles](https://opensearch.org/#principles)
21
+ * [Contributing to OpenSearch](CONTRIBUTING.md)
22
+ * [Maintainer Responsibilities](MAINTAINERS.md)
23
+ * [Release Management](RELEASING.md)
24
+ * [Admin Responsibilities](ADMINS.md)
25
+ * [Security](SECURITY.md)
26
+
27
+ ## Code of Conduct
28
+
29
+ This project has adopted the [Amazon Open Source Code of Conduct](CODE_OF_CONDUCT.md). For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq), or contact [opensource-codeofconduct@amazon.com](mailto:opensource-codeofconduct@amazon.com) with any additional questions or comments.
30
+
31
+ ## License
32
+
33
+ This project is licensed under the [Apache v2.0 License](LICENSE.txt).
34
+
35
+ ## Copyright
36
+
37
+ Copyright 2020-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
data/RELEASING.md ADDED
@@ -0,0 +1,36 @@
1
+ - [Overview](#overview)
2
+ - [Branching](#branching)
3
+ - [Release Branching](#release-branching)
4
+ - [Feature Branches](#feature-branches)
5
+ - [Release Labels](#release-labels)
6
+ - [Releasing](#releasing)
7
+
8
+ ## Overview
9
+
10
+ This document explains the release strategy for artifacts in this organization.
11
+
12
+ ## Branching
13
+
14
+ ### Release Branching
15
+
16
+ Given the current major release of 1.0, projects in this organization maintain the following active branches.
17
+
18
+ * **main**: The next _major_ release. This is the branch where all merges take place and code moves fast.
19
+ * **1.x**: The next _minor_ release. Once a change is merged into `main`, decide whether to backport it to `1.x`.
20
+ * **1.0**: The _current_ release. In between minor releases, only hotfixes (e.g. security) are backported to `1.0`.
21
+
22
+ Label PRs with the next major version label (e.g. `2.0.0`) and merge changes into `main`. Label PRs that you believe need to be backported as `1.x` and `1.0`. Backport PRs by checking out the versioned branch, cherry-pick changes and open a PR against each target backport branch.
23
+
24
+ ### Feature Branches
25
+
26
+ Do not creating branches in the upstream repo, use your fork, for the exception of long lasting feature branches that require active collaboration from multiple developers. Name feature branches `feature/<thing>`. Once the work is merged to `main`, please make sure to delete the feature branch.
27
+
28
+ ## Release Labels
29
+
30
+ Repositories create consistent release labels, such as `v1.0.0`, `v1.1.0` and `v2.0.0`, as well as `backport`. Use release labels to target an issue or a PR for a given release. See [MAINTAINERS](MAINTAINERS.md#triage-open-issues) for more information on triaging issues.
31
+
32
+ ## Releasing
33
+
34
+ The release process is standard across repositories in this org and is run by a release manager volunteering from amongst [MAINTAINERS](MAINTAINERS.md).
35
+
36
+ TODO
data/SECURITY.md ADDED
@@ -0,0 +1,3 @@
1
+ ## Reporting a Vulnerability
2
+
3
+ If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/) or directly via email to aws-security@amazon.com. Please do **not** create a public GitHub issue.
@@ -0,0 +1,449 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ #
3
+ # The OpenSearch Contributors require contributions made to
4
+ # this file be licensed under the Apache-2.0 license or a
5
+ # compatible open source license.
6
+ #
7
+ # Modifications Copyright OpenSearch Contributors. See
8
+ # GitHub history for details.
9
+
10
+ # encoding: utf-8
11
+ require "logstash/namespace"
12
+ require "logstash/environment"
13
+ require "logstash/outputs/base"
14
+ require "logstash/json"
15
+ require "concurrent/atomic/atomic_boolean"
16
+ require "stud/interval"
17
+ require "socket" # for Socket.gethostname
18
+ require "thread" # for safe queueing
19
+ require "uri" # for escaping user input
20
+ require "forwardable"
21
+
22
+ # This plugin is the recommended method of storing logs in OpenSearch.
23
+ # If you plan on using the OpenSearch Dashboards web interface, you'll want to use this output.
24
+ #
25
+ # This output only speaks the HTTP protocol. HTTP is the preferred protocol for interacting with OpenSearch.
26
+ # We strongly encourage the use of HTTP over the node protocol for a number of reasons. HTTP is only marginally slower,
27
+ # yet far easier to administer and work with. When using the HTTP protocol one may upgrade OpenSearch versions without having
28
+ # to upgrade Logstash in lock-step.
29
+ #
30
+ # You can learn more about OpenSearch at <https://opensearch.org/>
31
+ #
32
+ # ==== Retry Policy
33
+ # This plugin uses the OpenSearch bulk API to optimize its imports into OpenSearch. These requests may experience
34
+ # either partial or total failures.
35
+ #
36
+ # The following errors are retried infinitely:
37
+ #
38
+ # - Network errors (inability to connect)
39
+ # - 429 (Too many requests) and
40
+ # - 503 (Service unavailable) errors
41
+ #
42
+ # NOTE: 409 exceptions are no longer retried. Please set a higher `retry_on_conflict` value if you experience 409 exceptions.
43
+ # It is more performant for OpenSearch to retry these exceptions than this plugin.
44
+ #
45
+ # ==== Batch Sizes ====
46
+ # This plugin attempts to send batches of events as a single request. However, if
47
+ # a request exceeds 20MB we will break it up until multiple batch requests. If a single document exceeds 20MB it will be sent as a single request.
48
+ #
49
+ # ==== DNS Caching
50
+ #
51
+ # This plugin uses the JVM to lookup DNS entries and is subject to the value of https://docs.oracle.com/javase/7/docs/technotes/guides/net/properties.html[networkaddress.cache.ttl],
52
+ # a global setting for the JVM.
53
+ #
54
+ # As an example, to set your DNS TTL to 1 second you would set
55
+ # the `LS_JAVA_OPTS` environment variable to `-Dnetworkaddress.cache.ttl=1`.
56
+ #
57
+ # Keep in mind that a connection with keepalive enabled will
58
+ # not reevaluate its DNS value while the keepalive is in effect.
59
+ #
60
+ # ==== HTTP Compression
61
+ #
62
+ # This plugin supports request and response compression. Response compression is enabled by default,
63
+ # the user doesn't have to set any configs in OpenSearch for it to send back compressed response.
64
+ #
65
+ # For requests compression, users have to enable `http_compression`
66
+ # setting in their Logstash config file.
67
+ #
68
+ class LogStash::Outputs::OpenSearch < LogStash::Outputs::Base
69
+ declare_threadsafe!
70
+
71
+ require "logstash/outputs/opensearch/distribution_checker"
72
+ require "logstash/outputs/opensearch/http_client"
73
+ require "logstash/outputs/opensearch/http_client_builder"
74
+ require "logstash/plugin_mixins/opensearch/api_configs"
75
+ require "logstash/plugin_mixins/opensearch/common"
76
+ require 'logstash/plugin_mixins/ecs_compatibility_support'
77
+
78
+ # Protocol agnostic methods
79
+ include(LogStash::PluginMixins::OpenSearch::Common)
80
+
81
+ # ecs_compatibility option, provided by Logstash core or the support adapter.
82
+ include(LogStash::PluginMixins::ECSCompatibilitySupport)
83
+
84
+ # Generic/API config options that any document indexer output needs
85
+ include(LogStash::PluginMixins::OpenSearch::APIConfigs)
86
+
87
+ config_name "opensearch"
88
+
89
+ # The OpenSearch action to perform. Valid actions are:
90
+ #
91
+ # - index: indexes a document (an event from Logstash).
92
+ # - delete: deletes a document by id (An id is required for this action)
93
+ # - create: indexes a document, fails if a document by that id already exists in the index.
94
+ # - update: updates a document by id. Update has a special case where you can upsert -- update a
95
+ # document if not already present. See the `upsert` option.
96
+ # - A sprintf style string to change the action based on the content of the event. The value `%{[foo]}`
97
+ # would use the foo field for the action
98
+ #
99
+ # For more details on actions, check out the https://docs-beta.opensearch.org/opensearch/rest-api/bulk/[OpenSearch bulk API documentation]
100
+ config :action, :validate => :string, :default => "index"
101
+
102
+ # The index to write events to. This can be dynamic using the `%{foo}` syntax.
103
+ # The default value will partition your indices by day so you can more easily
104
+ # delete old data or only search specific date ranges.
105
+ # Indexes may not contain uppercase characters.
106
+ # For weekly indexes ISO 8601 format is recommended, eg. logstash-%{+xxxx.ww}.
107
+ # LS uses Joda to format the index pattern from event timestamp.
108
+ # Joda formats are defined http://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html[here].
109
+ config :index, :validate => :string
110
+
111
+ config :document_type,
112
+ :validate => :string,
113
+ :deprecated => "Document types are removed entirely. You should avoid this feature"
114
+
115
+ # From Logstash 1.3 onwards, a template is applied to OpenSearch during
116
+ # Logstash's startup if one with the name `template_name` does not already exist.
117
+ # By default, the contents of this template is the default template for
118
+ # `logstash-%{+YYYY.MM.dd}` which always matches indices based on the pattern
119
+ # `logstash-*`. Should you require support for other index names, or would like
120
+ # to change the mappings in the template in general, a custom template can be
121
+ # specified by setting `template` to the path of a template file.
122
+ #
123
+ # Setting `manage_template` to false disables this feature. If you require more
124
+ # control over template creation, (e.g. creating indices dynamically based on
125
+ # field names) you should set `manage_template` to false and use the REST
126
+ # API to apply your templates manually.
127
+ config :manage_template, :validate => :boolean, :default => true
128
+
129
+ # This configuration option defines how the template is named inside OpenSearch.
130
+ # Note that if you have used the template management features and subsequently
131
+ # change this, you will need to prune the old template manually, e.g.
132
+ #
133
+ # `curl -XDELETE <http://localhost:9200/_template/OldTemplateName?pretty>`
134
+ #
135
+ # where `OldTemplateName` is whatever the former setting was.
136
+ config :template_name, :validate => :string
137
+
138
+ # You can set the path to your own template here, if you so desire.
139
+ # If not set, the included template will be used.
140
+ config :template, :validate => :path
141
+
142
+ # The template_overwrite option will always overwrite the indicated template
143
+ # in OpenSearch with either the one indicated by template or the included one.
144
+ # This option is set to false by default. If you always want to stay up to date
145
+ # with the template provided by Logstash, this option could be very useful to you.
146
+ # Likewise, if you have your own template file managed by puppet, for example, and
147
+ # you wanted to be able to update it regularly, this option could help there as well.
148
+ #
149
+ # Please note that if you are using your own customized version of the Logstash
150
+ # template (logstash), setting this to true will make Logstash to overwrite
151
+ # the "logstash" template (i.e. removing all customized settings)
152
+ config :template_overwrite, :validate => :boolean, :default => false
153
+
154
+ # The version to use for indexing. Use sprintf syntax like `%{my_version}` to use a field value here.
155
+ config :version, :validate => :string
156
+
157
+ # The version_type to use for indexing.
158
+ config :version_type, :validate => ["internal", 'external', "external_gt", "external_gte", "force"]
159
+
160
+ # A routing override to be applied to all processed events.
161
+ # This can be dynamic using the `%{foo}` syntax.
162
+ config :routing, :validate => :string
163
+
164
+ # For child documents, ID of the associated parent.
165
+ # This can be dynamic using the `%{foo}` syntax.
166
+ config :parent, :validate => :string, :default => nil
167
+
168
+ # For child documents, name of the join field
169
+ config :join_field, :validate => :string, :default => nil
170
+
171
+ # Set upsert content for update mode.s
172
+ # Create a new document with this parameter as json string if `document_id` doesn't exists
173
+ config :upsert, :validate => :string, :default => ""
174
+
175
+ # Enable `doc_as_upsert` for update mode.
176
+ # Create a new document with source if `document_id` doesn't exist in OpenSearch
177
+ config :doc_as_upsert, :validate => :boolean, :default => false
178
+
179
+ # Set script name for scripted update mode
180
+ config :script, :validate => :string, :default => ""
181
+
182
+ # Define the type of script referenced by "script" variable
183
+ # inline : "script" contains inline script
184
+ # indexed : "script" contains the name of script directly indexed in opensearch
185
+ # file : "script" contains the name of script stored in opensearch's config directory
186
+ config :script_type, :validate => ["inline", 'indexed', "file"], :default => ["inline"]
187
+
188
+ # Set the language of the used script. If not set, this defaults to painless
189
+ config :script_lang, :validate => :string, :default => "painless"
190
+
191
+ # Set variable name passed to script (scripted update)
192
+ config :script_var_name, :validate => :string, :default => "event"
193
+
194
+ # if enabled, script is in charge of creating non-existent document (scripted update)
195
+ config :scripted_upsert, :validate => :boolean, :default => false
196
+
197
+ # The number of times OpenSearch should internally retry an update/upserted document
198
+ config :retry_on_conflict, :validate => :number, :default => 1
199
+
200
+ # Set which ingest pipeline you wish to execute for an event. You can also use event dependent configuration
201
+ # here like `pipeline => "%{INGEST_PIPELINE}"`
202
+ config :pipeline, :validate => :string, :default => nil
203
+
204
+ attr_reader :client
205
+ attr_reader :default_index
206
+ attr_reader :default_template_name
207
+
208
+ def initialize(*params)
209
+ super
210
+ setup_ecs_compatibility_related_defaults
211
+ end
212
+
213
+ def register
214
+ @after_successful_connection_done = Concurrent::AtomicBoolean.new(false)
215
+ @stopping = Concurrent::AtomicBoolean.new(false)
216
+
217
+ check_action_validity
218
+
219
+ @logger.info("New OpenSearch output", :class => self.class.name, :hosts => @hosts.map(&:sanitized).map(&:to_s))
220
+
221
+ @client = build_client(DistributionChecker.new(@logger))
222
+
223
+ @after_successful_connection_thread = after_successful_connection do
224
+ begin
225
+ finish_register
226
+ true # thread.value
227
+ rescue => e
228
+ # we do not want to halt the thread with an exception as that has consequences for LS
229
+ e # thread.value
230
+ ensure
231
+ @after_successful_connection_done.make_true
232
+ end
233
+ end
234
+
235
+ # To support BWC, we check if DLQ exists in core (< 5.4). If it doesn't, we use nil to resort to previous behavior.
236
+ @dlq_writer = dlq_enabled? ? execution_context.dlq_writer : nil
237
+
238
+ @event_mapper = -> (e) { event_action_tuple(e) }
239
+ @event_target = -> (e) { e.sprintf(@index) }
240
+
241
+ @bulk_request_metrics = metric.namespace(:bulk_requests)
242
+ @document_level_metrics = metric.namespace(:documents)
243
+ end
244
+
245
+ # @override post-register when OpenSearch connection established
246
+ def finish_register
247
+ discover_cluster_uuid
248
+ install_template
249
+ super
250
+ end
251
+
252
+ # @override to handle proxy => '' as if none was set
253
+ def config_init(params)
254
+ proxy = params['proxy']
255
+ if proxy.is_a?(String)
256
+ # environment variables references aren't yet resolved
257
+ proxy = deep_replace(proxy)
258
+ if proxy.empty?
259
+ params.delete('proxy')
260
+ @proxy = ''
261
+ else
262
+ params['proxy'] = proxy # do not do resolving again
263
+ end
264
+ end
265
+ super(params)
266
+ end
267
+
268
+ # Receive an array of events and immediately attempt to index them (no buffering)
269
+ def multi_receive(events)
270
+ wait_for_successful_connection if @after_successful_connection_done
271
+ retrying_submit map_events(events)
272
+ end
273
+
274
+ def map_events(events)
275
+ events.map(&@event_mapper)
276
+ end
277
+
278
+ def wait_for_successful_connection
279
+ after_successful_connection_done = @after_successful_connection_done
280
+ return unless after_successful_connection_done
281
+ stoppable_sleep 1 until after_successful_connection_done.true?
282
+
283
+ status = @after_successful_connection_thread && @after_successful_connection_thread.value
284
+ if status.is_a?(Exception) # check if thread 'halted' with an error
285
+ # keep logging that something isn't right (from every #multi_receive)
286
+ @logger.error "OpenSearch setup did not complete normally, please review previously logged errors",
287
+ message: status.message, exception: status.class
288
+ else
289
+ @after_successful_connection_done = nil # do not execute __method__ again if all went well
290
+ end
291
+ end
292
+ private :wait_for_successful_connection
293
+
294
+ def close
295
+ @stopping.make_true if @stopping
296
+ stop_after_successful_connection_thread
297
+ @client.close if @client
298
+ end
299
+
300
+ private
301
+
302
+ def stop_after_successful_connection_thread
303
+ @after_successful_connection_thread.join unless @after_successful_connection_thread.nil?
304
+ end
305
+
306
+ # Convert the event into a 3-tuple of action, params and event hash
307
+ def event_action_tuple(event)
308
+ params = common_event_params(event)
309
+ params[:_type] = get_event_type(event) if use_event_type?(nil)
310
+
311
+ if @parent
312
+ if @join_field
313
+ join_value = event.get(@join_field)
314
+ parent_value = event.sprintf(@parent)
315
+ event.set(@join_field, { "name" => join_value, "parent" => parent_value })
316
+ params[routing_field_name] = event.sprintf(@parent)
317
+ else
318
+ params[:parent] = event.sprintf(@parent)
319
+ end
320
+ end
321
+
322
+ action = event.sprintf(@action || 'index')
323
+
324
+ if action == 'update'
325
+ params[:_upsert] = LogStash::Json.load(event.sprintf(@upsert)) if @upsert != ""
326
+ params[:_script] = event.sprintf(@script) if @script != ""
327
+ params[retry_on_conflict_action_name] = @retry_on_conflict
328
+ end
329
+
330
+ params[:version] = event.sprintf(@version) if @version
331
+ params[:version_type] = event.sprintf(@version_type) if @version_type
332
+
333
+ EventActionTuple.new(action, params, event)
334
+ end
335
+
336
+ class EventActionTuple < Array # TODO: acting as an array for compatibility
337
+
338
+ def initialize(action, params, event, event_data = nil)
339
+ super(3)
340
+ self[0] = action
341
+ self[1] = params
342
+ self[2] = event_data || event.to_hash
343
+ @event = event
344
+ end
345
+
346
+ attr_reader :event
347
+
348
+ end
349
+
350
+ # @return Hash (initial) parameters for given event
351
+ # @private shared event params factory between index and data_stream mode
352
+ def common_event_params(event)
353
+ params = {
354
+ :_id => @document_id ? event.sprintf(@document_id) : nil,
355
+ :_index => @event_target.call(event),
356
+ routing_field_name => @routing ? event.sprintf(@routing) : nil
357
+ }
358
+
359
+ if @pipeline
360
+ value = event.sprintf(@pipeline)
361
+ # convention: empty string equates to not using a pipeline
362
+ # this is useful when using a field reference in the pipeline setting, e.g.
363
+ # opensearch {
364
+ # pipeline => "%{[@metadata][pipeline]}"
365
+ # }
366
+ params[:pipeline] = value unless value.empty?
367
+ end
368
+
369
+ params
370
+ end
371
+
372
+ @@plugins = Gem::Specification.find_all{|spec| spec.name =~ /logstash-output-opensearch-/ }
373
+
374
+ @@plugins.each do |plugin|
375
+ name = plugin.name.split('-')[-1]
376
+ require "logstash/outputs/opensearch/#{name}"
377
+ end
378
+
379
+ def retry_on_conflict_action_name
380
+ :retry_on_conflict
381
+ end
382
+
383
+ def routing_field_name
384
+ :routing
385
+ end
386
+
387
+ DEFAULT_EVENT_TYPE_ES = "_doc".freeze
388
+
389
+ def get_event_type(event)
390
+ # Set the 'type' value for the index.
391
+ type = if @document_type
392
+ event.sprintf(@document_type)
393
+ else
394
+ DEFAULT_EVENT_TYPE_ES
395
+ end
396
+
397
+ type.to_s
398
+ end
399
+
400
+ ##
401
+ # WARNING: This method is overridden in a subclass in Logstash Core 7.7-7.8's monitoring,
402
+ # where a `client` argument is both required and ignored. In later versions of
403
+ # Logstash Core it is optional and ignored, but to make it optional here would
404
+ # allow us to accidentally break compatibility with Logstashes where it was required.
405
+ # @param noop_required_client [nil]: required `nil` for legacy reasons.
406
+ # @return [Boolean]
407
+ def use_event_type?(noop_required_client)
408
+ # only if the user defined it
409
+ @document_type
410
+ end
411
+
412
+ def install_template
413
+ TemplateManager.install_template(self)
414
+ rescue => e
415
+ @logger.error("Failed to install template", message: e.message, exception: e.class, backtrace: e.backtrace)
416
+ end
417
+
418
+ def setup_ecs_compatibility_related_defaults
419
+ case ecs_compatibility
420
+ when :disabled
421
+ @default_index = "logstash-%{+yyyy.MM.dd}"
422
+ @default_template_name = 'logstash'
423
+ when :v1
424
+ @default_index = "ecs-logstash-%{+yyyy.MM.dd}"
425
+ @default_template_name = 'ecs-logstash'
426
+ else
427
+ fail("unsupported ECS Compatibility `#{ecs_compatibility}`")
428
+ end
429
+ @index ||= default_index
430
+ @template_name ||= default_template_name
431
+ end
432
+
433
+ # To be overidden by the -java version
434
+ VALID_HTTP_ACTIONS = ["index", "delete", "create", "update"]
435
+ def valid_actions
436
+ VALID_HTTP_ACTIONS
437
+ end
438
+
439
+ def check_action_validity
440
+ return if @action.nil? # not set
441
+ raise LogStash::ConfigurationError, "No action specified!" if @action.empty?
442
+
443
+ # If we're using string interpolation, we're good!
444
+ return if @action =~ /%{.+}/
445
+ return if valid_actions.include?(@action)
446
+
447
+ raise LogStash::ConfigurationError, "Action '#{@action}' is invalid! Pick one of #{valid_actions} or use a sprintf style statement"
448
+ end
449
+ end