logstash-output-elasticsearch 11.17.0-java → 11.18.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -0
- data/lib/logstash/outputs/elasticsearch/http_client/pool.rb +106 -61
- data/lib/logstash/outputs/elasticsearch/http_client.rb +1 -1
- data/lib/logstash/outputs/elasticsearch.rb +3 -1
- data/lib/logstash/plugin_mixins/elasticsearch/common.rb +3 -1
- data/logstash-output-elasticsearch.gemspec +1 -1
- data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +212 -111
- data/spec/unit/outputs/elasticsearch/http_client_spec.rb +1 -1
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: b42642e174e8a6f0bf30c67cbdf25e27cc8197f2aeb56cb2268fb11f65426a03
         | 
| 4 | 
            +
              data.tar.gz: 26ca32e908ef3d42ec4281259ea9a6bf31746031b12c14ad08fbfd189c069c3b
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: dd0b5731beb34c4e331a8ea52252c4b53af5d8c62ffb97c106eab961709d0d884a02a228d48a236eee635629d23a6c2243528a20e3f1f7368f609e232f26d7f7
         | 
| 7 | 
            +
              data.tar.gz: 79e980b61c3bdd1b3339f1f03eccff4c52fe596fc34e6069fac1239a5c67e17357d118a95d9536bc1937f930f1c60f935858c236447f5b87f8517ec9b79ef53e
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,3 +1,6 @@ | |
| 1 | 
            +
            ## 11.18.0
         | 
| 2 | 
            +
              - Added request header `Elastic-Api-Version` for serverless [#1147](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/1147)
         | 
| 3 | 
            +
             | 
| 1 4 | 
             
            ## 11.17.0
         | 
| 2 5 | 
             
              - Added support to http compression level. Deprecated `http_compression` in favour of `compression_level` and enabled compression level 1 by default. [#1148](https://github.com/logstash-plugins/logstash-output-elasticsearch/pull/1148)
         | 
| 3 6 |  | 
| @@ -16,6 +16,18 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient; | |
| 16 16 | 
             
                    @response_body = response_body
         | 
| 17 17 | 
             
                  end
         | 
| 18 18 |  | 
| 19 | 
            +
                  def invalid_eav_header?
         | 
| 20 | 
            +
                    @response_code == 400 && @response_body&.include?(ELASTIC_API_VERSION)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  def invalid_credentials?
         | 
| 24 | 
            +
                    @response_code == 401
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def forbidden?
         | 
| 28 | 
            +
                    @response_code == 403
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 19 31 | 
             
                end
         | 
| 20 32 | 
             
                class HostUnreachableError < Error;
         | 
| 21 33 | 
             
                  attr_reader :original_error, :url
         | 
| @@ -48,7 +60,9 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient; | |
| 48 60 | 
             
                  :sniffer_delay => 10,
         | 
| 49 61 | 
             
                }.freeze
         | 
| 50 62 |  | 
| 51 | 
            -
                 | 
| 63 | 
            +
                BUILD_FLAVOR_SERVERLESS = 'serverless'.freeze
         | 
| 64 | 
            +
                ELASTIC_API_VERSION = "Elastic-Api-Version".freeze
         | 
| 65 | 
            +
                DEFAULT_EAV_HEADER = { ELASTIC_API_VERSION => "2023-10-31" }.freeze
         | 
| 52 66 |  | 
| 53 67 | 
             
                def initialize(logger, adapter, initial_urls=[], options={})
         | 
| 54 68 | 
             
                  @logger = logger
         | 
| @@ -77,7 +91,7 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient; | |
| 77 91 | 
             
                  @license_checker = options[:license_checker] || LogStash::PluginMixins::ElasticSearch::NoopLicenseChecker::INSTANCE
         | 
| 78 92 |  | 
| 79 93 | 
             
                  @last_es_version = Concurrent::AtomicReference.new
         | 
| 80 | 
            -
                  @ | 
| 94 | 
            +
                  @build_flavor = Concurrent::AtomicReference.new
         | 
| 81 95 | 
             
                end
         | 
| 82 96 |  | 
| 83 97 | 
             
                def start
         | 
| @@ -232,39 +246,56 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient; | |
| 232 246 | 
             
                end
         | 
| 233 247 |  | 
| 234 248 | 
             
                def health_check_request(url)
         | 
| 235 | 
            -
                   | 
| 236 | 
            -
             | 
| 249 | 
            +
                  logger.debug("Running health check to see if an Elasticsearch connection is working",
         | 
| 250 | 
            +
                               :healthcheck_url => url.sanitized.to_s, :path => @healthcheck_path)
         | 
| 251 | 
            +
                  begin
         | 
| 252 | 
            +
                    response = perform_request_to_url(url, :head, @healthcheck_path)
         | 
| 253 | 
            +
                    return response, nil
         | 
| 254 | 
            +
                  rescue ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError => e
         | 
| 255 | 
            +
                    logger.warn("Health check failed", code: e.response_code, url: e.url, message: e.message)
         | 
| 256 | 
            +
                    return nil, e
         | 
| 257 | 
            +
                  end
         | 
| 237 258 | 
             
                end
         | 
| 238 259 |  | 
| 239 260 | 
             
                def healthcheck!(register_phase = true)
         | 
| 240 261 | 
             
                  # Try to keep locking granularity low such that we don't affect IO...
         | 
| 241 262 | 
             
                  @state_mutex.synchronize { @url_info.select {|url,meta| meta[:state] != :alive } }.each do |url,meta|
         | 
| 242 263 | 
             
                    begin
         | 
| 243 | 
            -
                       | 
| 244 | 
            -
             | 
| 245 | 
            -
                      health_check_request(url)
         | 
| 264 | 
            +
                      _, health_bad_code_err = health_check_request(url)
         | 
| 265 | 
            +
                      root_response, root_bad_code_err = get_root_path(url) if health_bad_code_err.nil? || register_phase
         | 
| 246 266 |  | 
| 247 267 | 
             
                      # when called from resurrectionist skip the product check done during register phase
         | 
| 248 268 | 
             
                      if register_phase
         | 
| 249 | 
            -
                         | 
| 250 | 
            -
             | 
| 251 | 
            -
                         | 
| 269 | 
            +
                        raise LogStash::ConfigurationError,
         | 
| 270 | 
            +
                              "Could not read Elasticsearch. Please check the credentials" if root_bad_code_err&.invalid_credentials?
         | 
| 271 | 
            +
                        raise LogStash::ConfigurationError,
         | 
| 272 | 
            +
                              "Could not read Elasticsearch. Please check the privileges" if root_bad_code_err&.forbidden?
         | 
| 273 | 
            +
                        # when customer_headers is invalid
         | 
| 274 | 
            +
                        raise LogStash::ConfigurationError,
         | 
| 275 | 
            +
                              "The Elastic-Api-Version header is not valid" if root_bad_code_err&.invalid_eav_header?
         | 
| 276 | 
            +
                        # when it is not Elasticserach
         | 
| 277 | 
            +
                        raise LogStash::ConfigurationError,
         | 
| 278 | 
            +
                              "Could not connect to a compatible version of Elasticsearch" if root_bad_code_err.nil? && !elasticsearch?(root_response)
         | 
| 279 | 
            +
             | 
| 280 | 
            +
                        test_serverless_connection(url, root_response)
         | 
| 252 281 | 
             
                      end
         | 
| 282 | 
            +
             | 
| 283 | 
            +
                      raise health_bad_code_err if health_bad_code_err
         | 
| 284 | 
            +
                      raise root_bad_code_err if root_bad_code_err
         | 
| 285 | 
            +
             | 
| 253 286 | 
             
                      # If no exception was raised it must have succeeded!
         | 
| 254 287 | 
             
                      logger.warn("Restored connection to ES instance", url: url.sanitized.to_s)
         | 
| 255 | 
            -
             | 
| 256 | 
            -
                       | 
| 257 | 
            -
                      es_version =  | 
| 258 | 
            -
                       | 
| 259 | 
            -
             | 
| 260 | 
            -
                      if es_version.nil?
         | 
| 261 | 
            -
             | 
| 262 | 
            -
                        next
         | 
| 263 | 
            -
                      end
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                      # We check its ES version
         | 
| 290 | 
            +
                      es_version, build_flavor = parse_es_version(root_response)
         | 
| 291 | 
            +
                      logger.warn("Failed to retrieve Elasticsearch build flavor") if build_flavor.nil?
         | 
| 292 | 
            +
                      logger.warn("Failed to retrieve Elasticsearch version data from connected endpoint, connection aborted", :url => url.sanitized.to_s) if es_version.nil?
         | 
| 293 | 
            +
                      next if es_version.nil?
         | 
| 294 | 
            +
             | 
| 264 295 | 
             
                      @state_mutex.synchronize do
         | 
| 265 296 | 
             
                        meta[:version] = es_version
         | 
| 266 297 | 
             
                        set_last_es_version(es_version, url)
         | 
| 267 | 
            -
                         | 
| 298 | 
            +
                        set_build_flavor(build_flavor)
         | 
| 268 299 |  | 
| 269 300 | 
             
                        alive = @license_checker.appropriate_license?(self, url)
         | 
| 270 301 | 
             
                        meta[:state] = alive ? :alive : :dead
         | 
| @@ -275,40 +306,21 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient; | |
| 275 306 | 
             
                  end
         | 
| 276 307 | 
             
                end
         | 
| 277 308 |  | 
| 278 | 
            -
                def  | 
| 309 | 
            +
                def get_root_path(url, params={})
         | 
| 279 310 | 
             
                  begin
         | 
| 280 | 
            -
                     | 
| 311 | 
            +
                    resp = perform_request_to_url(url, :get, ROOT_URI_PATH, params)
         | 
| 312 | 
            +
                    return resp, nil
         | 
| 281 313 | 
             
                  rescue ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError => e
         | 
| 282 | 
            -
                     | 
| 283 | 
            -
                     | 
| 314 | 
            +
                    logger.warn("Elasticsearch main endpoint returns #{e.response_code}", message: e.message, body: e.response_body)
         | 
| 315 | 
            +
                    return nil, e
         | 
| 284 316 | 
             
                  end
         | 
| 285 | 
            -
             | 
| 286 | 
            -
                  version_info = LogStash::Json.load(response.body)
         | 
| 287 | 
            -
                  return false if version_info['version'].nil?
         | 
| 288 | 
            -
             | 
| 289 | 
            -
                  version = ::Gem::Version.new(version_info["version"]['number'])
         | 
| 290 | 
            -
                  return false if version < ::Gem::Version.new('6.0.0')
         | 
| 291 | 
            -
             | 
| 292 | 
            -
                  if VERSION_6_TO_7.satisfied_by?(version)
         | 
| 293 | 
            -
                    return valid_tagline?(version_info)
         | 
| 294 | 
            -
                  elsif VERSION_7_TO_7_14.satisfied_by?(version)
         | 
| 295 | 
            -
                    build_flavor = version_info["version"]['build_flavor']
         | 
| 296 | 
            -
                    return false if build_flavor.nil? || build_flavor != 'default' || !valid_tagline?(version_info)
         | 
| 297 | 
            -
                  else
         | 
| 298 | 
            -
                    # case >= 7.14
         | 
| 299 | 
            -
                    lower_headers = response.headers.transform_keys {|key| key.to_s.downcase }
         | 
| 300 | 
            -
                    product_header = lower_headers['x-elastic-product']
         | 
| 301 | 
            -
                    return false if product_header != 'Elasticsearch'
         | 
| 302 | 
            -
                  end
         | 
| 303 | 
            -
                  return true
         | 
| 304 | 
            -
                rescue => e
         | 
| 305 | 
            -
                  logger.error("Unable to retrieve Elasticsearch version", url: url.sanitized.to_s, exception: e.class, message: e.message)
         | 
| 306 | 
            -
                  false
         | 
| 307 317 | 
             
                end
         | 
| 308 318 |  | 
| 309 | 
            -
                def  | 
| 310 | 
            -
                   | 
| 311 | 
            -
                   | 
| 319 | 
            +
                def test_serverless_connection(url, root_response)
         | 
| 320 | 
            +
                  _, build_flavor = parse_es_version(root_response)
         | 
| 321 | 
            +
                  params = { :headers => DEFAULT_EAV_HEADER }
         | 
| 322 | 
            +
                  _, bad_code_err = get_root_path(url, params) if build_flavor == BUILD_FLAVOR_SERVERLESS
         | 
| 323 | 
            +
                  raise LogStash::ConfigurationError, "The Elastic-Api-Version header is not valid" if bad_code_err&.invalid_eav_header?
         | 
| 312 324 | 
             
                end
         | 
| 313 325 |  | 
| 314 326 | 
             
                def stop_resurrectionist
         | 
| @@ -334,6 +346,7 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient; | |
| 334 346 | 
             
                end
         | 
| 335 347 |  | 
| 336 348 | 
             
                def perform_request_to_url(url, method, path, params={}, body=nil)
         | 
| 349 | 
            +
                  params[:headers] = DEFAULT_EAV_HEADER.merge(params[:headers] || {}) if serverless?
         | 
| 337 350 | 
             
                  @adapter.perform_request(url, method, path, params, body)
         | 
| 338 351 | 
             
                end
         | 
| 339 352 |  | 
| @@ -476,15 +489,6 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient; | |
| 476 489 | 
             
                  end
         | 
| 477 490 | 
             
                end
         | 
| 478 491 |  | 
| 479 | 
            -
                def get_es_version(url)
         | 
| 480 | 
            -
                  response = perform_request_to_url(url, :get, ROOT_URI_PATH)
         | 
| 481 | 
            -
                  return nil unless (200..299).cover?(response.code)
         | 
| 482 | 
            -
             | 
| 483 | 
            -
                  response = LogStash::Json.load(response.body)
         | 
| 484 | 
            -
             | 
| 485 | 
            -
                  response.fetch('version', {})
         | 
| 486 | 
            -
                end
         | 
| 487 | 
            -
             | 
| 488 492 | 
             
                def last_es_version
         | 
| 489 493 | 
             
                  @last_es_version.get
         | 
| 490 494 | 
             
                end
         | 
| @@ -494,7 +498,7 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient; | |
| 494 498 | 
             
                end
         | 
| 495 499 |  | 
| 496 500 | 
             
                def serverless?
         | 
| 497 | 
            -
                  @ | 
| 501 | 
            +
                  @build_flavor.get == BUILD_FLAVOR_SERVERLESS
         | 
| 498 502 | 
             
                end
         | 
| 499 503 |  | 
| 500 504 | 
             
                private
         | 
| @@ -526,9 +530,50 @@ module LogStash; module Outputs; class ElasticSearch; class HttpClient; | |
| 526 530 | 
             
                               previous_major: @maximum_seen_major_version, new_major: major, node_url: url.sanitized.to_s)
         | 
| 527 531 | 
             
                end
         | 
| 528 532 |  | 
| 529 | 
            -
                def  | 
| 530 | 
            -
                  @ | 
| 533 | 
            +
                def set_build_flavor(flavor)
         | 
| 534 | 
            +
                  @build_flavor.set(flavor)
         | 
| 535 | 
            +
                end
         | 
| 536 | 
            +
             | 
| 537 | 
            +
                def parse_es_version(response)
         | 
| 538 | 
            +
                  return nil, nil unless (200..299).cover?(response&.code)
         | 
| 539 | 
            +
             | 
| 540 | 
            +
                  response = LogStash::Json.load(response&.body)
         | 
| 541 | 
            +
                  version_info = response.fetch('version', {})
         | 
| 542 | 
            +
                  es_version = version_info.fetch('number', nil)
         | 
| 543 | 
            +
                  build_flavor = version_info.fetch('build_flavor', nil)
         | 
| 544 | 
            +
             | 
| 545 | 
            +
                  return es_version, build_flavor
         | 
| 546 | 
            +
                end
         | 
| 547 | 
            +
             | 
| 548 | 
            +
                def elasticsearch?(response)
         | 
| 549 | 
            +
                  return false if response.nil?
         | 
| 550 | 
            +
             | 
| 551 | 
            +
                  version_info = LogStash::Json.load(response.body)
         | 
| 552 | 
            +
                  return false if version_info['version'].nil?
         | 
| 553 | 
            +
             | 
| 554 | 
            +
                  version = ::Gem::Version.new(version_info["version"]['number'])
         | 
| 555 | 
            +
                  return false if version < ::Gem::Version.new('6.0.0')
         | 
| 556 | 
            +
             | 
| 557 | 
            +
                  if VERSION_6_TO_7.satisfied_by?(version)
         | 
| 558 | 
            +
                    return valid_tagline?(version_info)
         | 
| 559 | 
            +
                  elsif VERSION_7_TO_7_14.satisfied_by?(version)
         | 
| 560 | 
            +
                    build_flavor = version_info["version"]['build_flavor']
         | 
| 561 | 
            +
                    return false if build_flavor.nil? || build_flavor != 'default' || !valid_tagline?(version_info)
         | 
| 562 | 
            +
                  else
         | 
| 563 | 
            +
                    # case >= 7.14
         | 
| 564 | 
            +
                    lower_headers = response.headers.transform_keys {|key| key.to_s.downcase }
         | 
| 565 | 
            +
                    product_header = lower_headers['x-elastic-product']
         | 
| 566 | 
            +
                    return false if product_header != 'Elasticsearch'
         | 
| 567 | 
            +
                  end
         | 
| 568 | 
            +
                  return true
         | 
| 569 | 
            +
                rescue => e
         | 
| 570 | 
            +
                  logger.error("Unable to retrieve Elasticsearch version", exception: e.class, message: e.message)
         | 
| 571 | 
            +
                  false
         | 
| 531 572 | 
             
                end
         | 
| 532 573 |  | 
| 574 | 
            +
                def valid_tagline?(version_info)
         | 
| 575 | 
            +
                  tagline = version_info['tagline']
         | 
| 576 | 
            +
                  tagline == "You Know, for Search"
         | 
| 577 | 
            +
                end
         | 
| 533 578 | 
             
              end
         | 
| 534 579 | 
             
            end; end; end; end;
         | 
| @@ -596,7 +596,9 @@ class LogStash::Outputs::ElasticSearch < LogStash::Outputs::Base | |
| 596 596 | 
             
              def install_template
         | 
| 597 597 | 
             
                TemplateManager.install_template(self)
         | 
| 598 598 | 
             
              rescue => e
         | 
| 599 | 
            -
                 | 
| 599 | 
            +
                details = { message: e.message, exception: e.class, backtrace: e.backtrace }
         | 
| 600 | 
            +
                details[:body] = e.response_body if e.respond_to?(:response_body)
         | 
| 601 | 
            +
                @logger.error("Failed to install template", details)
         | 
| 600 602 | 
             
              end
         | 
| 601 603 |  | 
| 602 604 | 
             
              def setup_ecs_compatibility_related_defaults
         | 
| @@ -179,7 +179,9 @@ module LogStash; module PluginMixins; module ElasticSearch | |
| 179 179 | 
             
                  cluster_info = client.get('/')
         | 
| 180 180 | 
             
                  plugin_metadata.set(:cluster_uuid, cluster_info['cluster_uuid'])
         | 
| 181 181 | 
             
                rescue => e
         | 
| 182 | 
            -
                   | 
| 182 | 
            +
                  details = { message: e.message, exception: e.class, backtrace: e.backtrace }
         | 
| 183 | 
            +
                  details[:body] = e.response_body if e.respond_to?(:response_body)
         | 
| 184 | 
            +
                  @logger.error("Unable to retrieve Elasticsearch cluster uuid", details)
         | 
| 183 185 | 
             
                end
         | 
| 184 186 |  | 
| 185 187 | 
             
                def retrying_submit(actions)
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            Gem::Specification.new do |s|
         | 
| 2 2 | 
             
              s.name            = 'logstash-output-elasticsearch'
         | 
| 3 | 
            -
              s.version         = '11. | 
| 3 | 
            +
              s.version         = '11.18.0'
         | 
| 4 4 | 
             
              s.licenses        = ['apache-2.0']
         | 
| 5 5 | 
             
              s.summary         = "Stores logs in Elasticsearch"
         | 
| 6 6 | 
             
              s.description     = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
         | 
| @@ -7,8 +7,14 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do | |
| 7 7 | 
             
              let(:adapter) { LogStash::Outputs::ElasticSearch::HttpClient::ManticoreAdapter.new(logger, {}) }
         | 
| 8 8 | 
             
              let(:initial_urls) { [::LogStash::Util::SafeURI.new("http://localhost:9200")] }
         | 
| 9 9 | 
             
              let(:options) { {:resurrect_delay => 3, :url_normalizer => proc {|u| u}} } # Shorten the delay a bit to speed up tests
         | 
| 10 | 
            -
              let(:es_version_info) { [ { "number" => '0.0.0', "build_flavor" => 'default'} ] }
         | 
| 11 10 | 
             
              let(:license_status) { 'active' }
         | 
| 11 | 
            +
              let(:root_response) { MockResponse.new(200,
         | 
| 12 | 
            +
                                                      {"tagline" => "You Know, for Search",
         | 
| 13 | 
            +
                                                       "version" => {
         | 
| 14 | 
            +
                                                         "number" => '8.9.0',
         | 
| 15 | 
            +
                                                         "build_flavor" => 'default'} },
         | 
| 16 | 
            +
                                                      { "X-Elastic-Product" => "Elasticsearch" }
         | 
| 17 | 
            +
              ) }
         | 
| 12 18 |  | 
| 13 19 | 
             
              subject { described_class.new(logger, adapter, initial_urls, options) }
         | 
| 14 20 |  | 
| @@ -22,7 +28,6 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do | |
| 22 28 |  | 
| 23 29 | 
             
                allow(::Manticore::Client).to receive(:new).and_return(manticore_double)
         | 
| 24 30 |  | 
| 25 | 
            -
                allow(subject).to receive(:get_es_version).with(any_args).and_return(*es_version_info)
         | 
| 26 31 | 
             
                allow(subject.license_checker).to receive(:license_status).and_return(license_status)
         | 
| 27 32 | 
             
              end
         | 
| 28 33 |  | 
| @@ -37,35 +42,42 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do | |
| 37 42 | 
             
                end
         | 
| 38 43 | 
             
              end
         | 
| 39 44 |  | 
| 40 | 
            -
              describe " | 
| 41 | 
            -
                before(:each) { subject.start }
         | 
| 42 | 
            -
                it "should start the resurrectionist when created" do
         | 
| 43 | 
            -
                  expect(subject.resurrectionist_alive?).to eql(true)
         | 
| 44 | 
            -
                end
         | 
| 45 | 
            +
              describe "healthcheck" do
         | 
| 45 46 |  | 
| 46 | 
            -
                 | 
| 47 | 
            -
                   | 
| 48 | 
            -
                   | 
| 47 | 
            +
                describe "the resurrectionist" do
         | 
| 48 | 
            +
                  before(:each) { subject.start }
         | 
| 49 | 
            +
                  it "should start the resurrectionist when created" do
         | 
| 50 | 
            +
                    expect(subject.resurrectionist_alive?).to eql(true)
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  it "should attempt to resurrect connections after the ressurrect delay" do
         | 
| 54 | 
            +
                    expect(subject).to receive(:healthcheck!).once
         | 
| 55 | 
            +
                    sleep(subject.resurrect_delay + 1)
         | 
| 56 | 
            +
                  end
         | 
| 49 57 | 
             
                end
         | 
| 50 58 |  | 
| 51 | 
            -
                describe "healthcheck  | 
| 59 | 
            +
                describe "healthcheck path handling" do
         | 
| 52 60 | 
             
                  let(:initial_urls) { [::LogStash::Util::SafeURI.new("http://localhost:9200")] }
         | 
| 53 | 
            -
                  let(: | 
| 61 | 
            +
                  let(:healthcheck_response) { double("Response", :code => 200) }
         | 
| 54 62 |  | 
| 55 63 | 
             
                  before(:example) do
         | 
| 64 | 
            +
                    subject.start
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    expect(adapter).to receive(:perform_request).with(anything, :head, eq(healthcheck_path), anything, anything) do |url, _, _, _, _|
         | 
| 67 | 
            +
                      expect(url.path).to be_empty
         | 
| 68 | 
            +
                      healthcheck_response
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
             | 
| 56 71 | 
             
                    expect(adapter).to receive(:perform_request).with(anything, :get, "/", anything, anything) do |url, _, _, _, _|
         | 
| 57 72 | 
             
                      expect(url.path).to be_empty
         | 
| 73 | 
            +
                      root_response
         | 
| 58 74 | 
             
                    end
         | 
| 59 75 | 
             
                  end
         | 
| 60 76 |  | 
| 61 77 | 
             
                  context "and not setting healthcheck_path" do
         | 
| 78 | 
            +
                    let(:healthcheck_path) { "/" }
         | 
| 62 79 | 
             
                    it "performs the healthcheck to the root" do
         | 
| 63 | 
            -
                       | 
| 64 | 
            -
                        expect(url.path).to be_empty
         | 
| 65 | 
            -
             | 
| 66 | 
            -
                        success_response
         | 
| 67 | 
            -
                      end
         | 
| 68 | 
            -
                      expect { subject.healthcheck! }.to raise_error(LogStash::ConfigurationError, "Could not connect to a compatible version of Elasticsearch")
         | 
| 80 | 
            +
                      subject.healthcheck!
         | 
| 69 81 | 
             
                    end
         | 
| 70 82 | 
             
                  end
         | 
| 71 83 |  | 
| @@ -73,14 +85,116 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do | |
| 73 85 | 
             
                    let(:healthcheck_path) { "/my/health" }
         | 
| 74 86 | 
             
                    let(:options) { super().merge(:healthcheck_path => healthcheck_path) }
         | 
| 75 87 | 
             
                    it "performs the healthcheck to the healthcheck_path" do
         | 
| 76 | 
            -
                       | 
| 77 | 
            -
             | 
| 88 | 
            +
                      subject.healthcheck!
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
                  end
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                describe "register phase" do
         | 
| 94 | 
            +
                  shared_examples_for "root path returns bad code error" do |err_msg|
         | 
| 95 | 
            +
                    before :each do
         | 
| 96 | 
            +
                      subject.update_initial_urls
         | 
| 97 | 
            +
                      expect(subject).to receive(:elasticsearch?).never
         | 
| 98 | 
            +
                    end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                    it "raises ConfigurationError" do
         | 
| 101 | 
            +
                      expect(subject).to receive(:health_check_request).with(anything).and_return(["", nil])
         | 
| 102 | 
            +
                      expect(subject).to receive(:get_root_path).with(anything).and_return([nil,
         | 
| 103 | 
            +
                                                                                            ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError.new(mock_resp.code, nil, nil, mock_resp.body)])
         | 
| 104 | 
            +
                      expect { subject.healthcheck! }.to raise_error(LogStash::ConfigurationError, err_msg)
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                  context "with 200 without version" do
         | 
| 109 | 
            +
                    let(:mock_resp) { MockResponse.new(200, {"tagline" => "You Know, for Search"}) }
         | 
| 78 110 |  | 
| 79 | 
            -
             | 
| 80 | 
            -
                       | 
| 111 | 
            +
                    it "raises ConfigurationError" do
         | 
| 112 | 
            +
                      subject.update_initial_urls
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                      expect(subject).to receive(:health_check_request).with(anything).and_return(["", nil])
         | 
| 115 | 
            +
                      expect(subject).to receive(:get_root_path).with(anything).and_return([mock_resp, nil])
         | 
| 81 116 | 
             
                      expect { subject.healthcheck! }.to raise_error(LogStash::ConfigurationError, "Could not connect to a compatible version of Elasticsearch")
         | 
| 82 117 | 
             
                    end
         | 
| 83 118 | 
             
                  end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                  context "with 200 serverless" do
         | 
| 121 | 
            +
                    let(:good_resp) { MockResponse.new(200,
         | 
| 122 | 
            +
                                                       { "tagline" => "You Know, for Search",
         | 
| 123 | 
            +
                                                         "version" => { "number" => '8.10.0', "build_flavor" => 'serverless'}
         | 
| 124 | 
            +
                                                       },
         | 
| 125 | 
            +
                                                       { "X-Elastic-Product" => "Elasticsearch" }
         | 
| 126 | 
            +
                    ) }
         | 
| 127 | 
            +
                    let(:bad_400_err) do
         | 
| 128 | 
            +
                      ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError.new(400,
         | 
| 129 | 
            +
                       nil, nil,
         | 
| 130 | 
            +
                       "The requested [Elastic-Api-Version] header value of [2024-10-31] is not valid. Only [2023-10-31] is supported")
         | 
| 131 | 
            +
                    end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                    it "raises ConfigurationError when the serverless connection test fails" do
         | 
| 134 | 
            +
                      subject.update_initial_urls
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                      expect(subject).to receive(:health_check_request).with(anything).and_return(["", nil])
         | 
| 137 | 
            +
                      expect(subject).to receive(:get_root_path).with(anything).and_return([good_resp, nil])
         | 
| 138 | 
            +
                      expect(subject).to receive(:get_root_path).with(anything, hash_including(:headers => LogStash::Outputs::ElasticSearch::HttpClient::Pool::DEFAULT_EAV_HEADER)).and_return([nil, bad_400_err])
         | 
| 139 | 
            +
                      expect { subject.healthcheck! }.to raise_error(LogStash::ConfigurationError, "The Elastic-Api-Version header is not valid")
         | 
| 140 | 
            +
                    end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                    it "passes when the serverless connection test succeeds" do
         | 
| 143 | 
            +
                      subject.update_initial_urls
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                      expect(subject).to receive(:health_check_request).with(anything).and_return(["", nil])
         | 
| 146 | 
            +
                      expect(subject).to receive(:get_root_path).with(anything).and_return([good_resp, nil])
         | 
| 147 | 
            +
                      expect(subject).to receive(:get_root_path).with(anything, hash_including(:headers => LogStash::Outputs::ElasticSearch::HttpClient::Pool::DEFAULT_EAV_HEADER)).and_return([good_resp, nil])
         | 
| 148 | 
            +
                      expect { subject.healthcheck! }.not_to raise_error
         | 
| 149 | 
            +
                    end
         | 
| 150 | 
            +
                  end
         | 
| 151 | 
            +
             | 
| 152 | 
            +
                  context "with 200 default" do
         | 
| 153 | 
            +
                    let(:good_resp) { MockResponse.new(200,
         | 
| 154 | 
            +
                                                       { "tagline" => "You Know, for Search",
         | 
| 155 | 
            +
                                                         "version" => { "number" => '8.10.0', "build_flavor" => 'default'}
         | 
| 156 | 
            +
                                                       },
         | 
| 157 | 
            +
                                                       { "X-Elastic-Product" => "Elasticsearch" }
         | 
| 158 | 
            +
                    ) }
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                    it "passes without checking serverless connection" do
         | 
| 161 | 
            +
                      subject.update_initial_urls
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                      expect(subject).to receive(:health_check_request).with(anything).and_return(["", nil])
         | 
| 164 | 
            +
                      expect(subject).to receive(:get_root_path).with(anything).and_return([good_resp, nil])
         | 
| 165 | 
            +
                      expect(subject).not_to receive(:get_root_path).with(anything, hash_including(:headers => LogStash::Outputs::ElasticSearch::HttpClient::Pool::DEFAULT_EAV_HEADER))
         | 
| 166 | 
            +
                      expect { subject.healthcheck! }.not_to raise_error
         | 
| 167 | 
            +
                    end
         | 
| 168 | 
            +
                  end
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                  context "with 400" do
         | 
| 171 | 
            +
                    let(:mock_resp) { MockResponse.new(400, "The requested [Elastic-Api-Version] header value of [2024-10-31] is not valid. Only [2023-10-31] is supported") }
         | 
| 172 | 
            +
                    it_behaves_like "root path returns bad code error", "The Elastic-Api-Version header is not valid"
         | 
| 173 | 
            +
                  end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                  context "with 401" do
         | 
| 176 | 
            +
                    let(:mock_resp) { MockResponse.new(401, "missing authentication") }
         | 
| 177 | 
            +
                    it_behaves_like "root path returns bad code error", "Could not read Elasticsearch. Please check the credentials"
         | 
| 178 | 
            +
                  end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                  context "with 403" do
         | 
| 181 | 
            +
                    let(:mock_resp) { MockResponse.new(403, "Forbidden") }
         | 
| 182 | 
            +
                    it_behaves_like "root path returns bad code error", "Could not read Elasticsearch. Please check the privileges"
         | 
| 183 | 
            +
                  end
         | 
| 184 | 
            +
                end
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                describe "non register phase" do
         | 
| 187 | 
            +
                  let(:health_bad_code_err) { ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError.new(400, nil, nil, nil) }
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                  before :each do
         | 
| 190 | 
            +
                    subject.update_initial_urls
         | 
| 191 | 
            +
                  end
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                  it "does not call root path when health check request fails" do
         | 
| 194 | 
            +
                    expect(subject).to receive(:health_check_request).with(anything).and_return(["", health_bad_code_err])
         | 
| 195 | 
            +
                    expect(subject).to receive(:get_root_path).never
         | 
| 196 | 
            +
                    subject.healthcheck!(false)
         | 
| 197 | 
            +
                  end
         | 
| 84 198 | 
             
                end
         | 
| 85 199 | 
             
              end
         | 
| 86 200 |  | 
| @@ -251,23 +365,23 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do | |
| 251 365 | 
             
                  ::LogStash::Util::SafeURI.new("http://otherhost:9201")
         | 
| 252 366 | 
             
                ] }
         | 
| 253 367 |  | 
| 254 | 
            -
                let(: | 
| 255 | 
            -
             | 
| 256 | 
            -
             | 
| 257 | 
            -
             | 
| 258 | 
            -
             | 
| 259 | 
            -
             | 
| 260 | 
            -
             | 
| 261 | 
            -
             | 
| 262 | 
            -
             | 
| 263 | 
            -
                 | 
| 264 | 
            -
             | 
| 265 | 
            -
                it "picks the largest major version" do
         | 
| 266 | 
            -
                  expect(subject.maximum_seen_major_version).to eq(0)
         | 
| 267 | 
            -
                end
         | 
| 368 | 
            +
                let(:root_response) { MockResponse.new(200, {"tagline" => "You Know, for Search",
         | 
| 369 | 
            +
                                                              "version" => {
         | 
| 370 | 
            +
                                                                "number" => '0.0.0',
         | 
| 371 | 
            +
                                                                "build_flavor" => 'default'}
         | 
| 372 | 
            +
                }) }
         | 
| 373 | 
            +
                let(:root_response2) { MockResponse.new(200, {"tagline" => "You Know, for Search",
         | 
| 374 | 
            +
                                                              "version" => {
         | 
| 375 | 
            +
                                                                "number" => '6.0.0',
         | 
| 376 | 
            +
                                                                "build_flavor" => 'default'}
         | 
| 377 | 
            +
                }) }
         | 
| 268 378 |  | 
| 269 379 | 
             
                context "if there are nodes with multiple major versions" do
         | 
| 270 | 
            -
                   | 
| 380 | 
            +
                  before(:each) do
         | 
| 381 | 
            +
                    allow(subject).to receive(:perform_request_to_url).and_return(root_response, root_response2)
         | 
| 382 | 
            +
                    subject.start
         | 
| 383 | 
            +
                  end
         | 
| 384 | 
            +
             | 
| 271 385 | 
             
                  it "picks the largest major version" do
         | 
| 272 386 | 
             
                    expect(subject.maximum_seen_major_version).to eq(6)
         | 
| 273 387 | 
             
                  end
         | 
| @@ -275,32 +389,31 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do | |
| 275 389 | 
             
              end
         | 
| 276 390 |  | 
| 277 391 |  | 
| 278 | 
            -
              describe "build  | 
| 392 | 
            +
              describe "build flavor tracking" do
         | 
| 279 393 | 
             
                let(:initial_urls) { [::LogStash::Util::SafeURI.new("http://somehost:9200")] }
         | 
| 280 394 |  | 
| 281 | 
            -
                let(: | 
| 282 | 
            -
             | 
| 283 | 
            -
                let(:valid_response) { MockResponse.new(200,
         | 
| 395 | 
            +
                let(:root_response) { MockResponse.new(200,
         | 
| 284 396 | 
             
                                                        {"tagline" => "You Know, for Search",
         | 
| 285 397 | 
             
                                                              "version" => {
         | 
| 286 398 | 
             
                                                                "number" => '8.9.0',
         | 
| 287 | 
            -
                                                                "build_flavor" => LogStash::Outputs::ElasticSearch::HttpClient::Pool:: | 
| 399 | 
            +
                                                                "build_flavor" => LogStash::Outputs::ElasticSearch::HttpClient::Pool::BUILD_FLAVOR_SERVERLESS} },
         | 
| 288 400 | 
             
                                                        { "X-Elastic-Product" => "Elasticsearch" }
         | 
| 289 401 | 
             
                ) }
         | 
| 290 402 |  | 
| 291 403 | 
             
                before(:each) do
         | 
| 292 | 
            -
                  allow(subject).to receive(:perform_request_to_url).and_return( | 
| 404 | 
            +
                  allow(subject).to receive(:perform_request_to_url).and_return(root_response)
         | 
| 293 405 | 
             
                  subject.start
         | 
| 294 406 | 
             
                end
         | 
| 295 407 |  | 
| 296 | 
            -
                it "picks the build  | 
| 408 | 
            +
                it "picks the build flavor" do
         | 
| 297 409 | 
             
                  expect(subject.serverless?).to be_truthy
         | 
| 298 410 | 
             
                end
         | 
| 299 411 | 
             
              end
         | 
| 300 412 |  | 
| 301 413 | 
             
              describe "license checking" do
         | 
| 302 414 | 
             
                before(:each) do
         | 
| 303 | 
            -
                  allow(subject).to receive(:health_check_request)
         | 
| 415 | 
            +
                  allow(subject).to receive(:health_check_request).and_return(["", nil])
         | 
| 416 | 
            +
                  allow(subject).to receive(:perform_request_to_url).and_return(root_response)
         | 
| 304 417 | 
             
                  allow(subject).to receive(:elasticsearch?).and_return(true)
         | 
| 305 418 | 
             
                end
         | 
| 306 419 |  | 
| @@ -327,6 +440,36 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do | |
| 327 440 | 
             
                end
         | 
| 328 441 | 
             
              end
         | 
| 329 442 |  | 
| 443 | 
            +
              describe "elastic api version header" do
         | 
| 444 | 
            +
                let(:eav) { "Elastic-Api-Version" }
         | 
| 445 | 
            +
             | 
| 446 | 
            +
                context "when it is serverless" do
         | 
| 447 | 
            +
                  before(:each) do
         | 
| 448 | 
            +
                    expect(subject).to receive(:serverless?).and_return(true)
         | 
| 449 | 
            +
                  end
         | 
| 450 | 
            +
             | 
| 451 | 
            +
                  it "add the default header" do
         | 
| 452 | 
            +
                    expect(adapter).to receive(:perform_request).with(anything, :get, "/", anything, anything) do |_, _, _, params, _|
         | 
| 453 | 
            +
                      expect(params[:headers]).to eq({ "User-Agent" => "chromium",  "Elastic-Api-Version" => "2023-10-31"})
         | 
| 454 | 
            +
                    end
         | 
| 455 | 
            +
                    subject.perform_request_to_url(initial_urls, :get, "/", { :headers => { "User-Agent" => "chromium" }} )
         | 
| 456 | 
            +
                  end
         | 
| 457 | 
            +
                end
         | 
| 458 | 
            +
             | 
| 459 | 
            +
                context "when it is stateful" do
         | 
| 460 | 
            +
                  before(:each) do
         | 
| 461 | 
            +
                    expect(subject).to receive(:serverless?).and_return(false)
         | 
| 462 | 
            +
                  end
         | 
| 463 | 
            +
             | 
| 464 | 
            +
                  it "add the default header" do
         | 
| 465 | 
            +
                    expect(adapter).to receive(:perform_request).with(anything, :get, "/", anything, anything) do |_, _, _, params, _|
         | 
| 466 | 
            +
                      expect(params[:headers]).to be_nil
         | 
| 467 | 
            +
                    end
         | 
| 468 | 
            +
                    subject.perform_request_to_url(initial_urls, :get, "/" )
         | 
| 469 | 
            +
                  end
         | 
| 470 | 
            +
                end
         | 
| 471 | 
            +
              end
         | 
| 472 | 
            +
             | 
| 330 473 | 
             
              # TODO: extract to ElasticSearchOutputLicenseChecker unit spec
         | 
| 331 474 | 
             
              describe "license checking with ElasticSearchOutputLicenseChecker" do
         | 
| 332 475 | 
             
                let(:options) do
         | 
| @@ -334,7 +477,8 @@ describe LogStash::Outputs::ElasticSearch::HttpClient::Pool do | |
| 334 477 | 
             
                end
         | 
| 335 478 |  | 
| 336 479 | 
             
                before(:each) do
         | 
| 337 | 
            -
                  allow(subject).to receive(:health_check_request)
         | 
| 480 | 
            +
                  allow(subject).to receive(:health_check_request).and_return(["", nil])
         | 
| 481 | 
            +
                  allow(subject).to receive(:perform_request_to_url).and_return(root_response)
         | 
| 338 482 | 
             
                  allow(subject).to receive(:elasticsearch?).and_return(true)
         | 
| 339 483 | 
             
                end
         | 
| 340 484 |  | 
| @@ -388,114 +532,71 @@ describe "#elasticsearch?" do | |
| 388 532 | 
             
              let(:adapter) { double("Manticore Adapter") }
         | 
| 389 533 | 
             
              let(:initial_urls) { [::LogStash::Util::SafeURI.new("http://localhost:9200")] }
         | 
| 390 534 | 
             
              let(:options) { {:resurrect_delay => 2, :url_normalizer => proc {|u| u}} } # Shorten the delay a bit to speed up tests
         | 
| 391 | 
            -
              let(:es_version_info) { [{ "number" => '0.0.0', "build_flavor" => 'default'}] }
         | 
| 392 | 
            -
              let(:license_status) { 'active' }
         | 
| 393 535 |  | 
| 394 536 | 
             
              subject { LogStash::Outputs::ElasticSearch::HttpClient::Pool.new(logger, adapter, initial_urls, options) }
         | 
| 395 537 |  | 
| 396 | 
            -
              let(:url) { ::LogStash::Util::SafeURI.new("http://localhost:9200") }
         | 
| 397 | 
            -
             | 
| 398 | 
            -
              context "in case HTTP error code" do
         | 
| 399 | 
            -
                it "should fail for 401" do
         | 
| 400 | 
            -
                  allow(adapter).to receive(:perform_request)
         | 
| 401 | 
            -
                    .with(anything, :get, "/", anything, anything)
         | 
| 402 | 
            -
                    .and_return(MockResponse.new(401))
         | 
| 403 | 
            -
             | 
| 404 | 
            -
                  expect(subject.elasticsearch?(url)).to be false
         | 
| 405 | 
            -
                end
         | 
| 406 | 
            -
             | 
| 407 | 
            -
                it "should fail for 403" do
         | 
| 408 | 
            -
                  allow(adapter).to receive(:perform_request)
         | 
| 409 | 
            -
                          .with(anything, :get, "/", anything, anything)
         | 
| 410 | 
            -
                          .and_return(status: 403)
         | 
| 411 | 
            -
                  expect(subject.elasticsearch?(url)).to be false
         | 
| 412 | 
            -
                end
         | 
| 413 | 
            -
              end
         | 
| 414 | 
            -
             | 
| 415 538 | 
             
              context "when connecting to a cluster which reply without 'version' field" do
         | 
| 416 539 | 
             
                it "should fail" do
         | 
| 417 | 
            -
                   | 
| 418 | 
            -
             | 
| 419 | 
            -
                                .and_return(body: {"field" => "funky.com"}.to_json)
         | 
| 420 | 
            -
                  expect(subject.elasticsearch?(url)).to be false
         | 
| 540 | 
            +
                  resp = MockResponse.new(200, {"field" => "funky.com"} )
         | 
| 541 | 
            +
                  expect(subject.send(:elasticsearch?, resp)).to be false
         | 
| 421 542 | 
             
                end
         | 
| 422 543 | 
             
              end
         | 
| 423 544 |  | 
| 424 545 | 
             
              context "when connecting to a cluster with version < 6.0.0" do
         | 
| 425 546 | 
             
                it "should fail" do
         | 
| 426 | 
            -
                   | 
| 427 | 
            -
             | 
| 428 | 
            -
                                      .and_return(200, {"version" => { "number" => "5.0.0"}}.to_json)
         | 
| 429 | 
            -
                  expect(subject.elasticsearch?(url)).to be false
         | 
| 547 | 
            +
                  resp = MockResponse.new(200, {"version" => { "number" => "5.0.0" }})
         | 
| 548 | 
            +
                  expect(subject.send(:elasticsearch?, resp)).to be false
         | 
| 430 549 | 
             
                end
         | 
| 431 550 | 
             
              end
         | 
| 432 551 |  | 
| 433 552 | 
             
              context "when connecting to a cluster with version in [6.0.0..7.0.0)" do
         | 
| 434 553 | 
             
                it "must be successful with valid 'tagline'" do
         | 
| 435 | 
            -
                   | 
| 436 | 
            -
             | 
| 437 | 
            -
                                            .and_return(MockResponse.new(200, {"version" => {"number" => "6.5.0"}, "tagline" => "You Know, for Search"}))
         | 
| 438 | 
            -
                  expect(subject.elasticsearch?(url)).to be true
         | 
| 554 | 
            +
                  resp = MockResponse.new(200, {"version" => {"number" => "6.5.0"}, "tagline" => "You Know, for Search"} )
         | 
| 555 | 
            +
                  expect(subject.send(:elasticsearch?, resp)).to be true
         | 
| 439 556 | 
             
                end
         | 
| 440 557 |  | 
| 441 558 | 
             
                it "should fail if invalid 'tagline'" do
         | 
| 442 | 
            -
                   | 
| 443 | 
            -
             | 
| 444 | 
            -
                                            .and_return(MockResponse.new(200, {"version" => {"number" => "6.5.0"}, "tagline" => "You don't know"}))
         | 
| 445 | 
            -
                  expect(subject.elasticsearch?(url)).to be false
         | 
| 559 | 
            +
                  resp = MockResponse.new(200, {"version" => {"number" => "6.5.0"}, "tagline" => "You don't know"} )
         | 
| 560 | 
            +
                  expect(subject.send(:elasticsearch?, resp)).to be false
         | 
| 446 561 | 
             
                end
         | 
| 447 562 |  | 
| 448 563 | 
             
                it "should fail if 'tagline' is not present" do
         | 
| 449 | 
            -
                   | 
| 450 | 
            -
             | 
| 451 | 
            -
                                            .and_return(MockResponse.new(200, {"version" => {"number" => "6.5.0"}}))
         | 
| 452 | 
            -
                  expect(subject.elasticsearch?(url)).to be false
         | 
| 564 | 
            +
                  resp = MockResponse.new(200, {"version" => {"number" => "6.5.0"}} )
         | 
| 565 | 
            +
                  expect(subject.send(:elasticsearch?, resp)).to be false
         | 
| 453 566 | 
             
                end
         | 
| 454 567 | 
             
              end
         | 
| 455 568 |  | 
| 456 569 | 
             
              context "when connecting to a cluster with version in [7.0.0..7.14.0)" do
         | 
| 457 570 | 
             
                it "must be successful is 'build_flavor' is 'default' and tagline is correct" do
         | 
| 458 | 
            -
                   | 
| 459 | 
            -
             | 
| 460 | 
            -
                                            .and_return(MockResponse.new(200, {"version": {"number": "7.5.0", "build_flavor": "default"}, "tagline": "You Know, for Search"}))
         | 
| 461 | 
            -
                  expect(subject.elasticsearch?(url)).to be true
         | 
| 571 | 
            +
                  resp = MockResponse.new(200, {"version": {"number": "7.5.0", "build_flavor": "default"}, "tagline": "You Know, for Search"} )
         | 
| 572 | 
            +
                  expect(subject.send(:elasticsearch?, resp)).to be true
         | 
| 462 573 | 
             
                end
         | 
| 463 574 |  | 
| 464 575 | 
             
                it "should fail if 'build_flavor' is not 'default' and tagline is correct" do
         | 
| 465 | 
            -
                   | 
| 466 | 
            -
             | 
| 467 | 
            -
                                            .and_return(MockResponse.new(200, {"version": {"number": "7.5.0", "build_flavor": "oss"}, "tagline": "You Know, for Search"}))
         | 
| 468 | 
            -
                  expect(subject.elasticsearch?(url)).to be false
         | 
| 576 | 
            +
                  resp = MockResponse.new(200, {"version": {"number": "7.5.0", "build_flavor": "oss"}, "tagline": "You Know, for Search"} )
         | 
| 577 | 
            +
                  expect(subject.send(:elasticsearch?, resp)).to be false
         | 
| 469 578 | 
             
                end
         | 
| 470 579 |  | 
| 471 580 | 
             
                it "should fail if 'build_flavor' is not present and tagline is correct" do
         | 
| 472 | 
            -
                   | 
| 473 | 
            -
             | 
| 474 | 
            -
                                            .and_return(MockResponse.new(200, {"version": {"number": "7.5.0"}, "tagline": "You Know, for Search"}))
         | 
| 475 | 
            -
                  expect(subject.elasticsearch?(url)).to be false
         | 
| 581 | 
            +
                  resp = MockResponse.new(200, {"version": {"number": "7.5.0"}, "tagline": "You Know, for Search"} )
         | 
| 582 | 
            +
                  expect(subject.send(:elasticsearch?, resp)).to be false
         | 
| 476 583 | 
             
                end
         | 
| 477 584 | 
             
              end
         | 
| 478 585 |  | 
| 479 586 | 
             
              context "when connecting to a cluster with version >= 7.14.0" do
         | 
| 480 587 | 
             
                it "should fail if 'X-elastic-product' header is not present" do
         | 
| 481 | 
            -
                   | 
| 482 | 
            -
             | 
| 483 | 
            -
                                .and_return(MockResponse.new(200, {"version": {"number": "7.14.0"}}))
         | 
| 484 | 
            -
                  expect(subject.elasticsearch?(url)).to be false
         | 
| 588 | 
            +
                  resp = MockResponse.new(200, {"version": {"number": "7.14.0"}} )
         | 
| 589 | 
            +
                  expect(subject.send(:elasticsearch?, resp)).to be false
         | 
| 485 590 | 
             
                end
         | 
| 486 591 |  | 
| 487 592 | 
             
                it "should fail if 'X-elastic-product' header is present but with bad value" do
         | 
| 488 | 
            -
                   | 
| 489 | 
            -
             | 
| 490 | 
            -
                                .and_return(MockResponse.new(200, {"version": {"number": "7.14.0"}}, {'X-elastic-product' => 'not good'}))
         | 
| 491 | 
            -
                  expect(subject.elasticsearch?(url)).to be false
         | 
| 593 | 
            +
                  resp = MockResponse.new(200, {"version": {"number": "7.14.0"}}, {'X-elastic-product' => 'not good'} )
         | 
| 594 | 
            +
                  expect(subject.send(:elasticsearch?, resp)).to be false
         | 
| 492 595 | 
             
                end
         | 
| 493 596 |  | 
| 494 597 | 
             
                it "must be successful when 'X-elastic-product' header is present with 'Elasticsearch' value" do
         | 
| 495 | 
            -
                   | 
| 496 | 
            -
             | 
| 497 | 
            -
                                .and_return(MockResponse.new(200, {"version": {"number": "7.14.0"}}, {'X-elastic-product' => 'Elasticsearch'}))
         | 
| 498 | 
            -
                  expect(subject.elasticsearch?(url)).to be true
         | 
| 598 | 
            +
                  resp = MockResponse.new(200, {"version": {"number": "7.14.0"}}, {'X-elastic-product' => 'Elasticsearch'} )
         | 
| 599 | 
            +
                  expect(subject.send(:elasticsearch?, resp)).to be true
         | 
| 499 600 | 
             
                end
         | 
| 500 601 | 
             
              end
         | 
| 501 602 | 
             
            end
         | 
| @@ -135,7 +135,7 @@ describe LogStash::Outputs::ElasticSearch::HttpClient do | |
| 135 135 | 
             
                }
         | 
| 136 136 |  | 
| 137 137 | 
             
                it "returns the hash response" do
         | 
| 138 | 
            -
                  expect(subject.pool).to receive(:get).with(path | 
| 138 | 
            +
                  expect(subject.pool).to receive(:get).with(path).and_return(get_response)
         | 
| 139 139 | 
             
                  expect(subject.get(path)["body"]).to eq(body)
         | 
| 140 140 | 
             
                end
         | 
| 141 141 | 
             
              end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: logstash-output-elasticsearch
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 11. | 
| 4 | 
            +
              version: 11.18.0
         | 
| 5 5 | 
             
            platform: java
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Elastic
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2023-09- | 
| 11 | 
            +
            date: 2023-09-25 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              requirement: !ruby/object:Gem::Requirement
         |