# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one # or more contributor license agreements. Licensed under the Elastic License; # you may not use this file except in compliance with the Elastic License. require 'logstash/logging/logger' require 'logstash/outputs/elasticsearch' module LogStash module LicenseChecker class LicenseReader include LogStash::Util::Loggable XPACK_MISSING_STATUS_CODES = [400, 404] def initialize(settings, feature, options) @namespace = "xpack.#{feature}" @settings = settings es_options = options.merge('resurrect_delay' => 30) @es_options = Helpers::ElasticsearchOptions::es_options_with_product_origin_header(es_options) end ## # fetches an XPackInfo, or log and return nil if unavailable. # @return [XPathInfo, nil] def fetch_xpack_info retry_handler = ::LogStash::Helpers::LoggableTry.new(logger, 'validate Elasticsearch license') begin response = retry_handler.try(10.times, ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError) { client.get("_xpack") } # TODO: do we need both this AND the exception-based control flow?? return XPackInfo.xpack_not_installed if xpack_missing_response?(response) XPackInfo.from_es_response(response) rescue ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError => bad_response_error raise unless XPACK_MISSING_STATUS_CODES.include?(bad_response_error.response_code) XPackInfo.xpack_not_installed end rescue => e if logger.debug? logger.error('Unable to retrieve license information from license server', :message => e.message, :class => e.class.name, :backtrace => e.backtrace) else logger.error('Unable to retrieve license information from license server', :message => e.message) end XPackInfo.failed_to_fetch end def fetch_cluster_info retry_handler = ::LogStash::Helpers::LoggableTry.new(logger, 'fetch Elasticsearch cluster info') begin response = retry_handler.try(10.times, ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError) { client.get("/") } return response rescue => e logger.error("Unable to retrieve Elasticsearch cluster info.", message: e.message, exception: e.class) end {} end ## # @api private def client @client ||= build_client end private # # This is a bit of a hack until we refactor the ElasticSearch plugins # # and extract correctly the http client, right now I am using the plugins # # to deal with the certificates and the other SSL options # # # # But we have to silence the logger from the plugin, to make sure the # # log originate from the `ElasticsearchSource` def build_client es = LogStash::Outputs::ElasticSearch.new(@es_options) es.instance_variable_set :@logger, logger es.build_client end # Depending on the version Elasticsearch will return a 400 or a 404 response is xpack is not installed: # 5.x will return a 400, 6.x will return 404 def xpack_missing_response?(response) !!response['status'] && XPACK_MISSING_STATUS_CODES.include?(response['status']) end end end end