diff --git a/.github/draft-release-notes-config.yml b/.github/draft-release-notes-config.yml new file mode 100644 index 0000000..d168727 --- /dev/null +++ b/.github/draft-release-notes-config.yml @@ -0,0 +1,40 @@ +# The overall template of the release notes +template: | + Compatible with OpenSearch (**set version here**). + $CHANGES + +# Setting the formatting and sorting for the release notes body +name-template: Version (set version here) +change-template: '* $TITLE (#$NUMBER)' +sort-by: merged_at +sort-direction: ascending +replacers: + - search: '##' + replace: '###' + +# Organizing the tagged PRs into categories +categories: + - title: 'Breaking Changes' + labels: + - 'Breaking Changes' + - title: 'Features' + labels: + - 'Features' + - title: 'Enhancements' + labels: + - 'Enhancements' + - title: 'Bug Fixes' + labels: + - 'Bug Fixes' + - title: 'Infrastructure' + labels: + - 'Infrastructure' + - title: 'Documentation' + labels: + - 'Documentation' + - title: 'Maintenance' + labels: + - 'Maintenance' + - title: 'Refactoring' + labels: + - 'Refactoring' \ No newline at end of file diff --git a/.github/workflows/draft-release-notes-workflow.yml b/.github/workflows/draft-release-notes-workflow.yml new file mode 100644 index 0000000..6b3d89c --- /dev/null +++ b/.github/workflows/draft-release-notes-workflow.yml @@ -0,0 +1,20 @@ +name: Release Drafter + +on: + push: + branches: + - main + +jobs: + update_release_draft: + name: Update draft release notes + runs-on: ubuntu-latest + steps: + - name: Update draft release notes + uses: release-drafter/release-drafter@v5 + with: + config-name: draft-release-notes-config.yml + name: Version (set here) + tag: (None) + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 865bc1e..17f1aec 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ vendor/ /lib/logstash/outputs/elasticsearch/templates/ecs-v* *.iml /.idea/ +/dockerfiles/bin/ +/dockerfiles/.env \ No newline at end of file diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index 045fb0a..11d6aa8 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -201,7 +201,7 @@ Authentication to a secure OpenSearch cluster is possible by using username/pass ### Authorization -Authorization to a secure OpenSearch cluster requires read permission at [index level](https://docs-beta.opensearch.org/security-plugin/access-control/default-action-groups/#index-level). +Authorization to a secure OpenSearch cluster requires read permission at [index level](https://opensearch.org/docs/security-plugin/access-control/default-action-groups/#index-level). ## Submitting Changes diff --git a/README.md b/README.md index a260291..c545bda 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ ## Project Resources * [Project Website](https://opensearch.org/) -* [Documentation](https://docs-beta.opensearch.org/) +* [Documentation](https://opensearch.org/) * Need help? Try [Forums](https://discuss.opendistrocommunity.dev/) * [Project Principles](https://opensearch.org/#principles) * [Contributing to OpenSearch](CONTRIBUTING.md) diff --git a/dockerfiles/Dockerfile b/dockerfiles/Dockerfile new file mode 100644 index 0000000..4fda8bd --- /dev/null +++ b/dockerfiles/Dockerfile @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# +# Modifications Copyright OpenSearch Contributors. See +# GitHub history for details. + + +# Build arguments: +# VERSION: Optional. Specify the label for image. Defaults to 7.13.2 + +ARG VERSION +ARG ARCH +FROM docker.elastic.co/logstash/logstash-oss:${VERSION}-${ARCH} +USER logstash +COPY --chown=logstash:logstash dockerfiles/bin/logstash-output-opensearch-*.gem /tmp/logstash-output-opensearch.gem +COPY --chown=logstash:logstash dockerfiles/logstash-opensearch-sample.conf /usr/share/logstash/config/ +RUN /usr/share/logstash/bin/logstash-plugin install /tmp/logstash-output-opensearch.gem diff --git a/dockerfiles/build.sh b/dockerfiles/build.sh new file mode 100755 index 0000000..e425d3b --- /dev/null +++ b/dockerfiles/build.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# +# Modifications Copyright OpenSearch Contributors. See +# GitHub history for details. + +# This is intended to be run the plugin's root directory. `dockerfiles/build.sh` +# Ensure you have Docker installed locally and set the VERSION and BUILD_DATE environment variable. +set -e + +if [ -d dockerfiles/bin ]; then + rm -rf dockerfiles/bin +fi + +mkdir -p dockerfiles/bin + +echo 'Building plugin' +gem build logstash-output-opensearch.gemspec + +echo "Moving gem to bin directory" +mv logstash-output-opensearch*.gem dockerfiles/bin/ diff --git a/dockerfiles/docker-compose.yml b/dockerfiles/docker-compose.yml new file mode 100644 index 0000000..cc536a4 --- /dev/null +++ b/dockerfiles/docker-compose.yml @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# +# Modifications Copyright OpenSearch Contributors. See +# GitHub history for details. + +version: '3' + +services: + + logstash: + container_name: logstash-oss-${VERSION}${SUFFIX} + build: + context: ../ + dockerfile: dockerfiles/Dockerfile + args: + - VERSION=${LOGSTASH_VERSION:-7.13.2} + - ARCH=${ARCH} + image: "logstash-oss-with-opensearch-output-plugin:${VERSION}${SUFFIX}" diff --git a/dockerfiles/logstash-opensearch-sample.conf b/dockerfiles/logstash-opensearch-sample.conf new file mode 100644 index 0000000..fefca66 --- /dev/null +++ b/dockerfiles/logstash-opensearch-sample.conf @@ -0,0 +1,17 @@ +# Sample Logstash configuration for creating a simple +# Beats -> Logstash -> OpenSearch pipeline. + +input { + beats { + port => 5044 + } +} + +output { + opensearch { + hosts => ["http://localhost:9200"] + index => "%{[@metadata][beat]}-%{[@metadata][version]}-%{+YYYY.MM.dd}" + #user => "admin" + #password => "admin" + } +} diff --git a/dockerfiles/run.sh b/dockerfiles/run.sh new file mode 100755 index 0000000..2a06527 --- /dev/null +++ b/dockerfiles/run.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# +# Modifications Copyright OpenSearch Contributors. See +# GitHub history for details. + +# This is intended to be run the plugin's root directory. `dockerfiles/run.sh` +# Ensure you have Docker and docker-compose installed locally +set -e + +# Building plugin +./dockerfiles/build.sh + +# Identify Logstash version +version=${LOGSTASH_VERSION} +if [[ -z "$version" ]]; then + version=7.13.2 +fi +echo "VERSION=$version" > dockerfiles/.env + +# Identify Architecture +arch=`uname -m` +suffix='' +if [ $arch == 'arm64' ]; then + suffix='-arm64' +elif [ $arch == 'x86_64' ]; then + arch='amd64' + suffix='-x64' +else + echo "Unknown Architecture. Only amd64 and arm64 is supported" + exit 1 +fi +echo "ARCH=$arch" >> dockerfiles/.env +echo "SUFFIX=$suffix" >> dockerfiles/.env + +echo 'shutdown existing docker cluster' +docker-compose --file dockerfiles/docker-compose.yml down + +echo 'remove previous image to avoid conflict' +docker image rmi -f "logstash-oss-with-opensearch-output-plugin:$version$suffix" + +echo 'build new docker image with latest changes' +docker-compose --file dockerfiles/docker-compose.yml build + +#echo 'start docker cluster' +#docker-compose --file dockerfiles/docker-compose.yml up + +# steps to publish image to registry +# docker push ${DOCKER_NAMESPACE}/"logstash-oss-with-opensearch-output-plugin:$version$suffix" diff --git a/lib/logstash/outputs/opensearch.rb b/lib/logstash/outputs/opensearch.rb index d3f88e4..ff60bc7 100644 --- a/lib/logstash/outputs/opensearch.rb +++ b/lib/logstash/outputs/opensearch.rb @@ -68,6 +68,7 @@ class LogStash::Outputs::OpenSearch < LogStash::Outputs::Base declare_threadsafe! + require "logstash/outputs/opensearch/distribution_checker" require "logstash/outputs/opensearch/http_client" require "logstash/outputs/opensearch/http_client_builder" require "logstash/plugin_mixins/opensearch/api_configs" @@ -217,7 +218,7 @@ def register @logger.info("New OpenSearch output", :class => self.class.name, :hosts => @hosts.map(&:sanitized).map(&:to_s)) - @client = build_client + @client = build_client(DistributionChecker.new(@logger)) @after_successful_connection_thread = after_successful_connection do begin diff --git a/lib/logstash/outputs/opensearch/distribution_checker.rb b/lib/logstash/outputs/opensearch/distribution_checker.rb new file mode 100644 index 0000000..e7e4607 --- /dev/null +++ b/lib/logstash/outputs/opensearch/distribution_checker.rb @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# +# Modifications Copyright OpenSearch Contributors. See +# GitHub history for details. +# +module LogStash; module Outputs; class OpenSearch + class DistributionChecker + + def initialize(logger) + @logger = logger + end + + # Checks whether connecting cluster is one of supported distribution or not + # @param pool + # @param url [LogStash::Util::SafeURI] OpenSearch node URL + # @param major_version OpenSearch major version number + # @return [Boolean] true if supported + def is_supported?(pool, url, major_version) + case get_distribution(pool, url) + when 'opensearch' + return true + when 'oss' + if major_version == 7 + return true + end + end + log_incompatible_version(url) + false + end + + def get_distribution(pool, url) + pool.get_distribution(url) + end + + def log_incompatible_version(url) + @logger.error("Could not connect to cluster: incompatible version", url: url.sanitized.to_s) + end + end +end; end; end \ No newline at end of file diff --git a/lib/logstash/outputs/opensearch/http_client.rb b/lib/logstash/outputs/opensearch/http_client.rb index af09e88..be759bb 100644 --- a/lib/logstash/outputs/opensearch/http_client.rb +++ b/lib/logstash/outputs/opensearch/http_client.rb @@ -332,6 +332,7 @@ def build_pool(options) adapter = build_adapter(options) pool_options = { + :distribution_checker => options[:distribution_checker], :sniffing => sniffing, :sniffer_delay => options[:sniffer_delay], :sniffing_path => options[:sniffing_path], diff --git a/lib/logstash/outputs/opensearch/http_client/pool.rb b/lib/logstash/outputs/opensearch/http_client/pool.rb index cb16a21..6fee2d7 100644 --- a/lib/logstash/outputs/opensearch/http_client/pool.rb +++ b/lib/logstash/outputs/opensearch/http_client/pool.rb @@ -8,6 +8,7 @@ # GitHub history for details. require "concurrent/atomic/atomic_reference" +require "logstash/plugin_mixins/opensearch/noop_distribution_checker" module LogStash; module Outputs; class OpenSearch; class HttpClient; class Pool @@ -40,6 +41,7 @@ def message end attr_reader :logger, :adapter, :sniffing, :sniffer_delay, :resurrect_delay, :healthcheck_path, :sniffing_path, :bulk_path + attr_reader :distribution_checker ROOT_URI_PATH = '/'.freeze @@ -78,6 +80,7 @@ def initialize(logger, adapter, initial_urls=[], options={}) @stopping = false @last_version = Concurrent::AtomicReference.new + @distribution_checker = options[:distribution_checker] || LogStash::PluginMixins::OpenSearch::NoopDistributionChecker::INSTANCE end def start @@ -236,7 +239,8 @@ def healthcheck! @state_mutex.synchronize do meta[:version] = version set_last_version(version, url) - meta[:state] = :alive + alive = @distribution_checker.is_supported?(self, url, @maximum_seen_major_version) + meta[:state] = alive ? :alive : :dead end rescue HostUnreachableError, BadResponseCodeError => e logger.warn("Attempted to resurrect connection to dead OpenSearch instance, but got an error", url: url.sanitized.to_s, exception: e.class, message: e.message) @@ -411,9 +415,18 @@ def return_connection(url) end end - def get_version(url) + def get_version_map(url) request = perform_request_to_url(url, :get, ROOT_URI_PATH) - LogStash::Json.load(request.body)["version"]["number"] # e.g. "7.10.0" + LogStash::Json.load(request.body)['version'] + end + + def get_version(url) + get_version_map(url)['number'] # e.g. "7.10.0" + end + + def get_distribution(url) + version_map = get_version_map(url) + version_map.has_key?('distribution') ? version_map['distribution'] : version_map['build_flavor'] # e.g. "opensearch or oss" end def last_version @@ -432,7 +445,7 @@ def set_last_version(version, url) major = major_version(version) if @maximum_seen_major_version.nil? - @logger.info("OpenSearch version determined (#{version})", version: major) + @logger.info("Cluster version determined (#{version})", version: major) set_maximum_seen_major_version(major) elsif major > @maximum_seen_major_version warn_on_higher_major_version(major, url) @@ -441,7 +454,6 @@ def set_last_version(version, url) end def set_maximum_seen_major_version(major) - @logger.warn("the `type` event field won't be used to determine the document _type") @maximum_seen_major_version = major end diff --git a/lib/logstash/outputs/opensearch/http_client_builder.rb b/lib/logstash/outputs/opensearch/http_client_builder.rb index 7242b2e..282acce 100644 --- a/lib/logstash/outputs/opensearch/http_client_builder.rb +++ b/lib/logstash/outputs/opensearch/http_client_builder.rb @@ -24,6 +24,7 @@ def self.build(logger, hosts, params) client_settings[:proxy] = params["proxy"] if params["proxy"] common_options = { + :distribution_checker => params["distribution_checker"], :client_settings => client_settings, :metric => params["metric"], :resurrect_delay => params["resurrect_delay"] @@ -119,13 +120,21 @@ def self.setup_ssl(logger, params) return {:ssl => {:enabled => false}} if params["ssl"] == false - cacert, truststore, truststore_password, keystore, keystore_password = - params.values_at('cacert', 'truststore', 'truststore_password', 'keystore', 'keystore_password') + cacert, truststore, truststore_password, keystore, keystore_password, tls_client_cert, tls_client_key = + params.values_at('cacert', 'truststore', 'truststore_password', 'keystore', 'keystore_password', 'tls_certificate', 'tls_key') if cacert && truststore raise(LogStash::ConfigurationError, "Use either \"cacert\" or \"truststore\" when configuring the CA certificate") if truststore end + if (tls_client_cert && !tls_client_key) + raise(LogStash::ConfigurationError, "\"tls_key\" is missing") + end + + if (!tls_client_cert && tls_client_key) + raise(LogStash::ConfigurationError, "\"tls_certificate\" is missing") + end + ssl_options = {:enabled => true} if cacert @@ -139,6 +148,10 @@ def self.setup_ssl(logger, params) ssl_options[:keystore] = keystore ssl_options[:keystore_password] = keystore_password.value if keystore_password end + if (tls_client_cert && tls_client_key) + ssl_options[:client_cert] = tls_client_cert + ssl_options[:client_key] = tls_client_key + end if !params["ssl_certificate_verification"] logger.warn [ "** WARNING ** Detected UNSAFE options in opensearch output configuration!", diff --git a/lib/logstash/plugin_mixins/opensearch/api_configs.rb b/lib/logstash/plugin_mixins/opensearch/api_configs.rb index b1015f0..4ef1de5 100644 --- a/lib/logstash/plugin_mixins/opensearch/api_configs.rb +++ b/lib/logstash/plugin_mixins/opensearch/api_configs.rb @@ -66,6 +66,12 @@ module APIConfigs # Set the keystore password :keystore_password => { :validate => :password }, + # Set the TLS Client certificate file + :tls_certificate => { :validate => :path }, + + # Private key file name + :tls_key => { :validate => :path }, + # This setting asks OpenSearch for the list of all cluster nodes and adds them to the hosts list. # Note: This will return ALL nodes with HTTP enabled (including master nodes!). If you use # this with master nodes, you probably want to disable HTTP on them by setting diff --git a/lib/logstash/plugin_mixins/opensearch/common.rb b/lib/logstash/plugin_mixins/opensearch/common.rb index ee15360..4174f0c 100644 --- a/lib/logstash/plugin_mixins/opensearch/common.rb +++ b/lib/logstash/plugin_mixins/opensearch/common.rb @@ -24,8 +24,8 @@ module Common # Perform some OpenSearch options validations and Build the HttpClient. # Note that this methods may sets the @user, @password, @hosts and @client ivars as a side effect. # @return [HttpClient] the new http client - def build_client - + def build_client(distribution_checker=nil) + params["distribution_checker"] = distribution_checker # the following 3 options validation & setup methods are called inside build_client # because they must be executed prior to building the client and logstash # monitoring and management rely on directly calling build_client diff --git a/lib/logstash/plugin_mixins/opensearch/noop_distribution_checker.rb b/lib/logstash/plugin_mixins/opensearch/noop_distribution_checker.rb new file mode 100644 index 0000000..b24ad13 --- /dev/null +++ b/lib/logstash/plugin_mixins/opensearch/noop_distribution_checker.rb @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: Apache-2.0 +# +# The OpenSearch Contributors require contributions made to +# this file be licensed under the Apache-2.0 license or a +# compatible open source license. +# +# Modifications Copyright OpenSearch Contributors. See +# GitHub history for details. + +module LogStash; module PluginMixins; module OpenSearch + class NoopDistributionChecker + INSTANCE = self.new + + def is_supported?(pool, url, major_version) + true + end + end +end; end; end \ No newline at end of file diff --git a/logstash-output-opensearch.gemspec b/logstash-output-opensearch.gemspec index deaf439..167a949 100644 --- a/logstash-output-opensearch.gemspec +++ b/logstash-output-opensearch.gemspec @@ -19,7 +19,11 @@ Gem::Specification.new do |s| s.test_files = s.files.grep(%r{^(test|spec|features)/}) # Special flag to let us know this is actually a logstash plugin - s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output" } + s.metadata = { + "logstash_plugin" => "true", + "logstash_group" => "output", + "source_code_uri" => "https://github.com/opensearch-project/logstash-output-opensearch" + } s.add_runtime_dependency "manticore", '>= 0.5.4', '< 1.0.0' s.add_runtime_dependency 'stud', ['>= 0.0.17', '~> 0.0'] diff --git a/spec/unit/outputs/opensearch/http_client/pool_spec.rb b/spec/unit/outputs/opensearch/http_client/pool_spec.rb index a57ec0f..03593ff 100644 --- a/spec/unit/outputs/opensearch/http_client/pool_spec.rb +++ b/spec/unit/outputs/opensearch/http_client/pool_spec.rb @@ -16,7 +16,8 @@ let(:adapter) { LogStash::Outputs::OpenSearch::HttpClient::ManticoreAdapter.new(logger) } let(:initial_urls) { [::LogStash::Util::SafeURI.new("http://localhost:9200")] } let(:options) { {:resurrect_delay => 2, :url_normalizer => proc {|u| u}} } # Shorten the delay a bit to speed up tests - let(:node_versions) { [ "0.0.0" ] } + let(:node_versions) { [ "7.0.0" ] } + let(:get_distribution) { "oss" } subject { described_class.new(logger, adapter, initial_urls, options) } @@ -31,6 +32,7 @@ allow(::Manticore::Client).to receive(:new).and_return(manticore_double) allow(subject).to receive(:get_version).with(any_args).and_return(*node_versions) + allow(subject.distribution_checker).to receive(:get_distribution).and_return(get_distribution) end after do @@ -210,13 +212,94 @@ end it "picks the largest major version" do - expect(subject.maximum_seen_major_version).to eq(0) + expect(subject.maximum_seen_major_version).to eq(7) end context "if there are nodes with multiple major versions" do - let(:node_versions) { [ "0.0.0", "6.0.0" ] } + let(:node_versions) { [ "0.0.0", "7.0.0" ] } it "picks the largest major version" do - expect(subject.maximum_seen_major_version).to eq(6) + expect(subject.maximum_seen_major_version).to eq(7) + end + end + end + describe "distribution checking" do + before(:each) do + allow(subject).to receive(:health_check_request) + end + + let(:options) do + super().merge(:distribution_checker => distribution_checker) + end + + context 'when DistributionChecker#is_supported? returns false' do + let(:distribution_checker) { double('DistributionChecker', :is_supported? => false) } + + it 'does not mark the URL as active' do + subject.update_initial_urls + expect(subject.alive_urls_count).to eq(0) + end + end + + context 'when DistributionChecker#is_supported? returns true' do + let(:distribution_checker) { double('DistributionChecker', :is_supported? => true) } + + it 'marks the URL as active' do + subject.update_initial_urls + expect(subject.alive_urls_count).to eq(1) + end + end + end + describe 'distribution checking with cluster output' do + let(:options) do + super().merge(:distribution_checker => LogStash::Outputs::OpenSearch::DistributionChecker.new(logger)) + end + + before(:each) do + allow(subject).to receive(:health_check_request) + end + + context 'when using opensearch' do + + context "if cluster doesn't return a valid distribution" do + let(:get_distribution) { nil } + + it 'marks the url as dead' do + subject.update_initial_urls + expect(subject.alive_urls_count).to eq(0) + end + + it 'logs message' do + expect(subject.distribution_checker).to receive(:log_incompatible_version).once.and_call_original + subject.update_initial_urls + end + end + + context 'if cluster returns opensearch' do + let(:get_distribution) { 'opensearch' } + + it "marks the url as active" do + subject.update_initial_urls + expect(subject.alive_urls_count).to eq(1) + end + + it 'does not log message' do + expect(subject.distribution_checker).to_not receive(:log_incompatible_version) + subject.update_initial_urls + end + end + + context 'if cluster returns oss' do + let(:get_distribution) { 'oss' } + + it 'marks the url as active' do + subject.update_initial_urls + expect(subject.alive_urls_count).to eq(1) + end + + it 'does not log message' do + expect(subject.distribution_checker).to_not receive(:log_incompatible_version) + subject.update_initial_urls + end end end end diff --git a/spec/unit/outputs/opensearch_ssl_spec.rb b/spec/unit/outputs/opensearch_ssl_spec.rb index 9be7b68..79811ee 100644 --- a/spec/unit/outputs/opensearch_ssl_spec.rb +++ b/spec/unit/outputs/opensearch_ssl_spec.rb @@ -87,4 +87,93 @@ end end + + context "when using ssl with client certificates and key" do + let(:tls_certificate) { Stud::Temporary.file.path } + let(:tls_key) { Stud::Temporary.file.path } + before do + `openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{tls_key} -out #{tls_certificate}` + end + + after :each do + File.delete(tls_key) + File.delete(tls_certificate) + subject.close + end + + subject do + require "logstash/outputs/opensearch" + settings = { + "hosts" => "node01", + "ssl" => true, + "tls_certificate" => tls_certificate, + "tls_key" => tls_key + } + next LogStash::Outputs::OpenSearch.new(settings) + end + + it "should pass the tls certificate parameters to the OpenSearch client" do + expect(::Manticore::Client).to receive(:new) do |args| + expect(args[:ssl]).to include(:client_cert => tls_certificate, :client_key => tls_key) + end.and_call_original + subject.register + end + + end + + context "passing only tls certificate but missing the key" do + let(:tls_certificate) { Stud::Temporary.file.path } + let(:tls_key) { Stud::Temporary.file.path } + before do + `openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{tls_key} -out #{tls_certificate}` + end + + after :each do + File.delete(tls_key) + File.delete(tls_certificate) + subject.close + end + + subject do + settings = { + "hosts" => "node01", + "ssl" => true, + "tls_certificate" => tls_certificate, + } + next LogStash::Outputs::OpenSearch.new(settings) + end + + it "should not load plugin" do + expect { subject.register }.to raise_error(LogStash::ConfigurationError) + end + end + + context "passing only tls key but missing the certificate" do + let(:tls_certificate) { Stud::Temporary.file.path } + let(:tls_key) { Stud::Temporary.file.path } + before do + `openssl req -x509 -batch -nodes -newkey rsa:2048 -keyout #{tls_key} -out #{tls_certificate}` + end + + after :each do + File.delete(tls_key) + File.delete(tls_certificate) + subject.close + end + + subject do + settings = { + "hosts" => "node01", + "ssl" => true, + "tls_key" => tls_key, + } + next LogStash::Outputs::OpenSearch.new(settings) + end + + it "should not load plugin" do + expect { subject.register }.to raise_error(LogStash::ConfigurationError) + end + end + + end