logstash-output-elasticsearch-test 11.16.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.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +649 -0
  3. data/CONTRIBUTORS +34 -0
  4. data/Gemfile +16 -0
  5. data/LICENSE +202 -0
  6. data/NOTICE.TXT +5 -0
  7. data/README.md +106 -0
  8. data/docs/index.asciidoc +1369 -0
  9. data/lib/logstash/outputs/elasticsearch/data_stream_support.rb +282 -0
  10. data/lib/logstash/outputs/elasticsearch/default-ilm-policy.json +14 -0
  11. data/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb +155 -0
  12. data/lib/logstash/outputs/elasticsearch/http_client/pool.rb +534 -0
  13. data/lib/logstash/outputs/elasticsearch/http_client.rb +497 -0
  14. data/lib/logstash/outputs/elasticsearch/http_client_builder.rb +201 -0
  15. data/lib/logstash/outputs/elasticsearch/ilm.rb +92 -0
  16. data/lib/logstash/outputs/elasticsearch/license_checker.rb +52 -0
  17. data/lib/logstash/outputs/elasticsearch/template_manager.rb +131 -0
  18. data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-6x.json +45 -0
  19. data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-7x.json +44 -0
  20. data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-8x.json +50 -0
  21. data/lib/logstash/outputs/elasticsearch.rb +699 -0
  22. data/lib/logstash/plugin_mixins/elasticsearch/api_configs.rb +237 -0
  23. data/lib/logstash/plugin_mixins/elasticsearch/common.rb +409 -0
  24. data/lib/logstash/plugin_mixins/elasticsearch/noop_license_checker.rb +9 -0
  25. data/logstash-output-elasticsearch.gemspec +40 -0
  26. data/spec/es_spec_helper.rb +225 -0
  27. data/spec/fixtures/_nodes/6x.json +81 -0
  28. data/spec/fixtures/_nodes/7x.json +92 -0
  29. data/spec/fixtures/htpasswd +2 -0
  30. data/spec/fixtures/license_check/active.json +16 -0
  31. data/spec/fixtures/license_check/inactive.json +5 -0
  32. data/spec/fixtures/nginx_reverse_proxy.conf +22 -0
  33. data/spec/fixtures/scripts/painless/scripted_update.painless +2 -0
  34. data/spec/fixtures/scripts/painless/scripted_update_nested.painless +1 -0
  35. data/spec/fixtures/scripts/painless/scripted_upsert.painless +1 -0
  36. data/spec/fixtures/template-with-policy-es6x.json +48 -0
  37. data/spec/fixtures/template-with-policy-es7x.json +45 -0
  38. data/spec/fixtures/template-with-policy-es8x.json +50 -0
  39. data/spec/fixtures/test_certs/ca.crt +29 -0
  40. data/spec/fixtures/test_certs/ca.der.sha256 +1 -0
  41. data/spec/fixtures/test_certs/ca.key +51 -0
  42. data/spec/fixtures/test_certs/renew.sh +13 -0
  43. data/spec/fixtures/test_certs/test.crt +30 -0
  44. data/spec/fixtures/test_certs/test.der.sha256 +1 -0
  45. data/spec/fixtures/test_certs/test.key +51 -0
  46. data/spec/fixtures/test_certs/test.p12 +0 -0
  47. data/spec/fixtures/test_certs/test_invalid.crt +36 -0
  48. data/spec/fixtures/test_certs/test_invalid.key +51 -0
  49. data/spec/fixtures/test_certs/test_invalid.p12 +0 -0
  50. data/spec/fixtures/test_certs/test_self_signed.crt +32 -0
  51. data/spec/fixtures/test_certs/test_self_signed.key +54 -0
  52. data/spec/fixtures/test_certs/test_self_signed.p12 +0 -0
  53. data/spec/integration/outputs/compressed_indexing_spec.rb +70 -0
  54. data/spec/integration/outputs/create_spec.rb +67 -0
  55. data/spec/integration/outputs/data_stream_spec.rb +68 -0
  56. data/spec/integration/outputs/delete_spec.rb +63 -0
  57. data/spec/integration/outputs/ilm_spec.rb +534 -0
  58. data/spec/integration/outputs/index_spec.rb +421 -0
  59. data/spec/integration/outputs/index_version_spec.rb +98 -0
  60. data/spec/integration/outputs/ingest_pipeline_spec.rb +75 -0
  61. data/spec/integration/outputs/metrics_spec.rb +66 -0
  62. data/spec/integration/outputs/no_es_on_startup_spec.rb +78 -0
  63. data/spec/integration/outputs/painless_update_spec.rb +99 -0
  64. data/spec/integration/outputs/parent_spec.rb +94 -0
  65. data/spec/integration/outputs/retry_spec.rb +182 -0
  66. data/spec/integration/outputs/routing_spec.rb +61 -0
  67. data/spec/integration/outputs/sniffer_spec.rb +94 -0
  68. data/spec/integration/outputs/templates_spec.rb +133 -0
  69. data/spec/integration/outputs/unsupported_actions_spec.rb +75 -0
  70. data/spec/integration/outputs/update_spec.rb +114 -0
  71. data/spec/spec_helper.rb +10 -0
  72. data/spec/support/elasticsearch/api/actions/delete_ilm_policy.rb +19 -0
  73. data/spec/support/elasticsearch/api/actions/get_alias.rb +18 -0
  74. data/spec/support/elasticsearch/api/actions/get_ilm_policy.rb +18 -0
  75. data/spec/support/elasticsearch/api/actions/put_alias.rb +24 -0
  76. data/spec/support/elasticsearch/api/actions/put_ilm_policy.rb +25 -0
  77. data/spec/unit/http_client_builder_spec.rb +185 -0
  78. data/spec/unit/outputs/elasticsearch/data_stream_support_spec.rb +612 -0
  79. data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +151 -0
  80. data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +501 -0
  81. data/spec/unit/outputs/elasticsearch/http_client_spec.rb +339 -0
  82. data/spec/unit/outputs/elasticsearch/template_manager_spec.rb +189 -0
  83. data/spec/unit/outputs/elasticsearch_proxy_spec.rb +103 -0
  84. data/spec/unit/outputs/elasticsearch_spec.rb +1573 -0
  85. data/spec/unit/outputs/elasticsearch_ssl_spec.rb +197 -0
  86. data/spec/unit/outputs/error_whitelist_spec.rb +56 -0
  87. data/spec/unit/outputs/license_check_spec.rb +57 -0
  88. metadata +423 -0
@@ -0,0 +1,92 @@
1
+ module LogStash; module Outputs; class ElasticSearch
2
+ module Ilm
3
+
4
+ ILM_POLICY_PATH = "default-ilm-policy.json"
5
+
6
+ def setup_ilm
7
+ logger.warn("Overwriting supplied index #{@index} with rollover alias #{@ilm_rollover_alias}") unless default_index?(@index)
8
+ @index = @ilm_rollover_alias
9
+ maybe_create_rollover_alias
10
+ maybe_create_ilm_policy
11
+ end
12
+
13
+ def ilm_in_use?
14
+ return @ilm_actually_enabled if defined?(@ilm_actually_enabled)
15
+ @ilm_actually_enabled =
16
+ begin
17
+ if serverless?
18
+ raise LogStash::ConfigurationError, "Invalid ILM configuration `ilm_enabled => true`. " +
19
+ "Serverless Elasticsearch cluster does not support Index Lifecycle Management." if @ilm_enabled.to_s == 'true'
20
+ @logger.info("ILM auto configuration (`ilm_enabled => auto` or unset) resolved to `false`. "\
21
+ "Serverless Elasticsearch cluster does not support Index Lifecycle Management.") if @ilm_enabled == 'auto'
22
+ false
23
+ elsif @ilm_enabled == 'auto'
24
+ if ilm_on_by_default?
25
+ ilm_alias_set?
26
+ else
27
+ @logger.info("ILM auto configuration (`ilm_enabled => auto` or unset) resolved to `false`."\
28
+ " Elasticsearch cluster is before 7.0.0, which is the minimum version required to automatically run Index Lifecycle Management")
29
+ false
30
+ end
31
+ elsif @ilm_enabled.to_s == 'true'
32
+ ilm_alias_set?
33
+ else
34
+ false
35
+ end
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def ilm_alias_set?
42
+ default_index?(@index) || !default_rollover_alias?(@ilm_rollover_alias)
43
+ end
44
+
45
+ def ilm_on_by_default?
46
+ maximum_seen_major_version >= 7
47
+ end
48
+
49
+ def default_index?(index)
50
+ index == @default_index
51
+ end
52
+
53
+ def default_rollover_alias?(rollover_alias)
54
+ rollover_alias == default_ilm_rollover_alias
55
+ end
56
+
57
+ def ilm_policy_default?
58
+ ilm_policy == LogStash::Outputs::ElasticSearch::DEFAULT_POLICY
59
+ end
60
+
61
+ def maybe_create_ilm_policy
62
+ if ilm_policy_default?
63
+ client.ilm_policy_put(ilm_policy, policy_payload) unless client.ilm_policy_exists?(ilm_policy)
64
+ else
65
+ raise LogStash::ConfigurationError, "The specified ILM policy #{ilm_policy} does not exist on your Elasticsearch instance" unless client.ilm_policy_exists?(ilm_policy)
66
+ end
67
+ end
68
+
69
+ def maybe_create_rollover_alias
70
+ client.rollover_alias_put(rollover_alias_target, rollover_alias_payload) unless client.rollover_alias_exists?(ilm_rollover_alias)
71
+ end
72
+
73
+ def rollover_alias_target
74
+ "<#{ilm_rollover_alias}-#{ilm_pattern}>"
75
+ end
76
+
77
+ def rollover_alias_payload
78
+ {
79
+ 'aliases' => {
80
+ ilm_rollover_alias =>{
81
+ 'is_write_index' => true
82
+ }
83
+ }
84
+ }
85
+ end
86
+
87
+ def policy_payload
88
+ policy_path = ::File.expand_path(ILM_POLICY_PATH, ::File.dirname(__FILE__))
89
+ LogStash::Json.load(::IO.read(policy_path))
90
+ end
91
+ end
92
+ end; end; end
@@ -0,0 +1,52 @@
1
+ module LogStash; module Outputs; class ElasticSearch
2
+ class LicenseChecker
3
+
4
+ def initialize(logger)
5
+ @logger = logger
6
+ end
7
+
8
+ # Figure out if the provided license is appropriate or not
9
+ # The appropriate_license? methods is the method called from LogStash::Outputs::ElasticSearch::HttpClient::Pool#healthcheck!
10
+ # @param pool
11
+ # @param url [LogStash::Util::SafeURI] ES node URL
12
+ # @return [Boolean] true if provided license is deemed appropriate
13
+ def appropriate_license?(pool, url)
14
+ return true if pool.serverless?
15
+
16
+ license = extract_license(pool.get_license(url))
17
+ case license_status(license)
18
+ when 'active'
19
+ true
20
+ when nil
21
+ warn_no_license(url)
22
+ false
23
+ else # 'invalid', 'expired'
24
+ warn_invalid_license(url, license)
25
+ true
26
+ end
27
+ end
28
+
29
+ NO_LICENSE = {}.freeze
30
+ private_constant :NO_LICENSE
31
+
32
+ def extract_license(license)
33
+ license.fetch("license", NO_LICENSE)
34
+ end
35
+
36
+ def license_status(license)
37
+ license.fetch("status", nil)
38
+ end
39
+
40
+ private
41
+
42
+ def warn_no_license(url)
43
+ @logger.error("Could not connect to a compatible version of Elasticsearch", url: url.sanitized.to_s)
44
+ end
45
+
46
+ def warn_invalid_license(url, license)
47
+ @logger.warn("Elasticsearch license is not active, please check Elasticsearch’s licensing information",
48
+ url: url.sanitized.to_s, license: license)
49
+ end
50
+
51
+ end
52
+ end; end; end
@@ -0,0 +1,131 @@
1
+ module LogStash; module Outputs; class ElasticSearch
2
+ class TemplateManager
3
+ LEGACY_TEMPLATE_ENDPOINT = '_template'.freeze
4
+ INDEX_TEMPLATE_ENDPOINT = '_index_template'.freeze
5
+
6
+ # To be mixed into the elasticsearch plugin base
7
+ def self.install_template(plugin)
8
+ return unless plugin.manage_template
9
+
10
+ if plugin.maximum_seen_major_version < 8 && plugin.template_api == 'auto'
11
+ plugin.logger.warn("`template_api => auto` resolved to `legacy` since we are connected to " + "Elasticsearch #{plugin.maximum_seen_major_version}, " +
12
+ "but will resolve to `composable` the first time it connects to Elasticsearch 8+. " +
13
+ "We recommend either setting `template_api => legacy` to continue providing legacy-style templates, " +
14
+ "or migrating your template to the composable style and setting `template_api => composable`. " +
15
+ "The legacy template API is slated for removal in Elasticsearch 9.")
16
+ elsif plugin.template_api == 'legacy' && plugin.serverless?
17
+ raise LogStash::ConfigurationError, "Invalid template configuration `template_api => legacy`. Serverless Elasticsearch does not support legacy template API."
18
+ end
19
+
20
+
21
+ if plugin.template
22
+ plugin.logger.info("Using mapping template from", :path => plugin.template)
23
+ template = read_template_file(plugin.template)
24
+ else
25
+ plugin.logger.info("Using a default mapping template", :es_version => plugin.maximum_seen_major_version,
26
+ :ecs_compatibility => plugin.ecs_compatibility)
27
+ template = load_default_template(plugin.maximum_seen_major_version, plugin.ecs_compatibility)
28
+ end
29
+
30
+ add_ilm_settings_to_template(plugin, template) if plugin.ilm_in_use?
31
+ plugin.logger.debug("Attempting to install template", template: template)
32
+ install(plugin.client, template_endpoint(plugin), template_name(plugin), template, plugin.template_overwrite)
33
+ end
34
+
35
+ private
36
+ def self.load_default_template(es_major_version, ecs_compatibility)
37
+ template_path = default_template_path(es_major_version, ecs_compatibility)
38
+ read_template_file(template_path)
39
+ rescue => e
40
+ fail "Failed to load default template for Elasticsearch v#{es_major_version} with ECS #{ecs_compatibility}; caused by: #{e.inspect}"
41
+ end
42
+
43
+ def self.install(client, template_endpoint, template_name, template, template_overwrite)
44
+ client.template_install(template_endpoint, template_name, template, template_overwrite)
45
+ end
46
+
47
+ def self.add_ilm_settings_to_template(plugin, template)
48
+ # Overwrite any index patterns, and use the rollover alias. Use 'index_patterns' rather than 'template' for pattern
49
+ # definition - remove any existing definition of 'template'
50
+ template.delete('template') if template.include?('template') if plugin.maximum_seen_major_version < 8
51
+ template['index_patterns'] = "#{plugin.ilm_rollover_alias}-*"
52
+ settings = resolve_template_settings(plugin, template)
53
+ if settings && (settings['index.lifecycle.name'] || settings['index.lifecycle.rollover_alias'])
54
+ plugin.logger.info("Overwriting index lifecycle name and rollover alias as ILM is enabled")
55
+ end
56
+ settings.update({ 'index.lifecycle.name' => plugin.ilm_policy, 'index.lifecycle.rollover_alias' => plugin.ilm_rollover_alias})
57
+ end
58
+
59
+ def self.resolve_template_settings(plugin, template)
60
+ if template.key?('template')
61
+ plugin.logger.trace("Resolving ILM template settings: under 'template' key", :template => template, :template_api => plugin.template_api, :es_version => plugin.maximum_seen_major_version)
62
+ composable_index_template_settings(template)
63
+ elsif template.key?('settings')
64
+ plugin.logger.trace("Resolving ILM template settings: under 'settings' key", :template => template, :template_api => plugin.template_api, :es_version => plugin.maximum_seen_major_version)
65
+ legacy_index_template_settings(template)
66
+ else
67
+ use_index_template_api = index_template_api?(plugin)
68
+ plugin.logger.trace("Resolving ILM template settings: template doesn't have 'settings' or 'template' fields, falling back to auto detection", :template => template, :template_api => plugin.template_api, :es_version => plugin.maximum_seen_major_version, :index_template_api => use_index_template_api)
69
+ if use_index_template_api
70
+ composable_index_template_settings(template)
71
+ else
72
+ legacy_index_template_settings(template)
73
+ end
74
+ end
75
+ end
76
+
77
+ # Sets ['settings'] field to be compatible with _template API structure
78
+ def self.legacy_index_template_settings(template)
79
+ template['settings'] ||= {}
80
+ end
81
+
82
+ # Sets the ['template']['settings'] fields if not exist to be compatible with _index_template API structure
83
+ def self.composable_index_template_settings(template)
84
+ template['template'] ||= {}
85
+ template['template']['settings'] ||= {}
86
+ end
87
+
88
+ # Template name - if template_name set, use it
89
+ # if not and ILM is enabled, use the rollover alias
90
+ # else use the default value of template_name
91
+ def self.template_name(plugin)
92
+ plugin.ilm_in_use? && !plugin.original_params.key?('template_name') ? plugin.ilm_rollover_alias : plugin.template_name
93
+ end
94
+
95
+ def self.default_template_path(es_major_version, ecs_compatibility=:disabled)
96
+ template_version = es_major_version
97
+ default_template_name = "templates/ecs-#{ecs_compatibility}/elasticsearch-#{template_version}x.json"
98
+ ::File.expand_path(default_template_name, ::File.dirname(__FILE__))
99
+ end
100
+
101
+ def self.read_template_file(template_path)
102
+ raise ArgumentError, "Template file '#{template_path}' could not be found" unless ::File.exists?(template_path)
103
+ template_data = ::IO.read(template_path)
104
+ LogStash::Json.load(template_data)
105
+ end
106
+
107
+ def self.template_endpoint(plugin)
108
+ index_template_api?(plugin) ? INDEX_TEMPLATE_ENDPOINT : LEGACY_TEMPLATE_ENDPOINT
109
+ end
110
+
111
+ def self.index_template_api?(plugin)
112
+ case plugin.serverless?
113
+ when true
114
+ true
115
+ else
116
+ case plugin.template_api
117
+ when 'auto'
118
+ plugin.maximum_seen_major_version >= 8
119
+ when 'composable'
120
+ true
121
+ when 'legacy'
122
+ false
123
+ else
124
+ plugin.logger.warn("Invalid template_api value #{plugin.template_api}")
125
+ true
126
+ end
127
+ end
128
+ end
129
+
130
+ end
131
+ end end end
@@ -0,0 +1,45 @@
1
+ {
2
+ "template" : "logstash-*",
3
+ "version" : 60001,
4
+ "settings" : {
5
+ "index.refresh_interval" : "5s"
6
+ },
7
+ "mappings" : {
8
+ "_default_" : {
9
+ "dynamic_templates" : [ {
10
+ "message_field" : {
11
+ "path_match" : "message",
12
+ "match_mapping_type" : "string",
13
+ "mapping" : {
14
+ "type" : "text",
15
+ "norms" : false
16
+ }
17
+ }
18
+ }, {
19
+ "string_fields" : {
20
+ "match" : "*",
21
+ "match_mapping_type" : "string",
22
+ "mapping" : {
23
+ "type" : "text", "norms" : false,
24
+ "fields" : {
25
+ "keyword" : { "type": "keyword", "ignore_above": 256 }
26
+ }
27
+ }
28
+ }
29
+ } ],
30
+ "properties" : {
31
+ "@timestamp": { "type": "date"},
32
+ "@version": { "type": "keyword"},
33
+ "geoip" : {
34
+ "dynamic": true,
35
+ "properties" : {
36
+ "ip": { "type": "ip" },
37
+ "location" : { "type" : "geo_point" },
38
+ "latitude" : { "type" : "half_float" },
39
+ "longitude" : { "type" : "half_float" }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "index_patterns" : "logstash-*",
3
+ "version" : 60001,
4
+ "settings" : {
5
+ "index.refresh_interval" : "5s",
6
+ "number_of_shards": 1
7
+ },
8
+ "mappings" : {
9
+ "dynamic_templates" : [ {
10
+ "message_field" : {
11
+ "path_match" : "message",
12
+ "match_mapping_type" : "string",
13
+ "mapping" : {
14
+ "type" : "text",
15
+ "norms" : false
16
+ }
17
+ }
18
+ }, {
19
+ "string_fields" : {
20
+ "match" : "*",
21
+ "match_mapping_type" : "string",
22
+ "mapping" : {
23
+ "type" : "text", "norms" : false,
24
+ "fields" : {
25
+ "keyword" : { "type": "keyword", "ignore_above": 256 }
26
+ }
27
+ }
28
+ }
29
+ } ],
30
+ "properties" : {
31
+ "@timestamp": { "type": "date"},
32
+ "@version": { "type": "keyword"},
33
+ "geoip" : {
34
+ "dynamic": true,
35
+ "properties" : {
36
+ "ip": { "type": "ip" },
37
+ "location" : { "type" : "geo_point" },
38
+ "latitude" : { "type" : "half_float" },
39
+ "longitude" : { "type" : "half_float" }
40
+ }
41
+ }
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "index_patterns" : "logstash-*",
3
+ "version" : 80001,
4
+ "template" : {
5
+ "settings" : {
6
+ "index.refresh_interval" : "5s",
7
+ "number_of_shards": 1
8
+ },
9
+ "mappings" : {
10
+ "dynamic_templates" : [ {
11
+ "message_field" : {
12
+ "path_match" : "message",
13
+ "match_mapping_type" : "string",
14
+ "mapping" : {
15
+ "type" : "text",
16
+ "norms" : false
17
+ }
18
+ }
19
+ }, {
20
+ "string_fields" : {
21
+ "match" : "*",
22
+ "match_mapping_type" : "string",
23
+ "mapping" : {
24
+ "type" : "text", "norms" : false,
25
+ "fields" : {
26
+ "keyword" : { "type": "keyword", "ignore_above": 256 }
27
+ }
28
+ }
29
+ }
30
+ } ],
31
+ "properties" : {
32
+ "@timestamp": { "type": "date" },
33
+ "@version": { "type": "keyword" },
34
+ "geoip" : {
35
+ "dynamic": true,
36
+ "properties" : {
37
+ "ip": { "type": "ip" },
38
+ "location" : { "type" : "geo_point" },
39
+ "latitude" : { "type" : "half_float" },
40
+ "longitude" : { "type" : "half_float" }
41
+ }
42
+ }
43
+ }
44
+ }
45
+ },
46
+ "priority": 200,
47
+ "_meta" : {
48
+ "description": "index template for logstash-output-elasticsearch"
49
+ }
50
+ }