From ceade6d50997eeb1aa138869af6730a7a7ec2270 Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Thu, 16 Sep 2021 09:32:11 +0100 Subject: [PATCH 01/11] [CLIENT] Removes elasticsearch-transport --- Gemfile | 1 - elasticsearch-transport/.gitignore | 17 - elasticsearch-transport/Gemfile | 38 - elasticsearch-transport/LICENSE.txt | 202 -- elasticsearch-transport/README.md | 635 ------- elasticsearch-transport/Rakefile | 80 - .../elasticsearch-transport.gemspec | 75 - .../lib/elasticsearch-transport.rb | 18 - .../lib/elasticsearch/transport.rb | 37 - .../lib/elasticsearch/transport/client.rb | 282 --- .../elasticsearch/transport/meta_header.rb | 135 -- .../lib/elasticsearch/transport/redacted.rb | 75 - .../elasticsearch/transport/transport/base.rb | 450 ----- .../transport/connections/collection.rb | 127 -- .../transport/connections/connection.rb | 160 -- .../transport/connections/selector.rb | 92 - .../transport/transport/errors.rb | 91 - .../transport/transport/http/curb.rb | 121 -- .../transport/transport/http/faraday.rb | 96 - .../transport/transport/http/manticore.rb | 179 -- .../transport/transport/loggable.rb | 85 - .../transport/transport/response.rb | 37 - .../transport/serializer/multi_json.rb | 53 - .../transport/transport/sniffer.rb | 102 - .../lib/elasticsearch/transport/version.rb | 22 - .../connections/collection_spec.rb | 266 --- .../connections/selector_spec.rb | 174 -- .../spec/elasticsearch/transport/base_spec.rb | 264 --- .../elasticsearch/transport/client_spec.rb | 1661 ----------------- .../transport/meta_header_spec.rb | 274 --- .../elasticsearch/transport/sniffer_spec.rb | 275 --- elasticsearch-transport/spec/spec_helper.rb | 91 - .../test/integration/jruby_test.rb | 39 - .../test/integration/transport_test.rb | 90 - .../test/profile/client_benchmark_test.rb | 132 -- elasticsearch-transport/test/test_helper.rb | 88 - .../test/unit/connection_test.rb | 135 -- .../test/unit/response_test.rb | 30 - .../test/unit/serializer_test.rb | 33 - .../test/unit/transport_base_test.rb | 664 ------- .../test/unit/transport_curb_test.rb | 135 -- .../test/unit/transport_faraday_test.rb | 228 --- .../test/unit/transport_manticore_test.rb | 251 --- 43 files changed, 8040 deletions(-) delete mode 100644 elasticsearch-transport/.gitignore delete mode 100644 elasticsearch-transport/Gemfile delete mode 100644 elasticsearch-transport/LICENSE.txt delete mode 100644 elasticsearch-transport/README.md delete mode 100644 elasticsearch-transport/Rakefile delete mode 100644 elasticsearch-transport/elasticsearch-transport.gemspec delete mode 100644 elasticsearch-transport/lib/elasticsearch-transport.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/client.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/meta_header.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/redacted.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/transport/base.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/transport/connections/collection.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/transport/connections/connection.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/transport/connections/selector.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/transport/errors.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/transport/http/curb.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/transport/http/faraday.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/transport/http/manticore.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/transport/loggable.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/transport/response.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/transport/serializer/multi_json.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/transport/sniffer.rb delete mode 100644 elasticsearch-transport/lib/elasticsearch/transport/version.rb delete mode 100644 elasticsearch-transport/spec/elasticsearch/connections/collection_spec.rb delete mode 100644 elasticsearch-transport/spec/elasticsearch/connections/selector_spec.rb delete mode 100644 elasticsearch-transport/spec/elasticsearch/transport/base_spec.rb delete mode 100644 elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb delete mode 100644 elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb delete mode 100644 elasticsearch-transport/spec/elasticsearch/transport/sniffer_spec.rb delete mode 100644 elasticsearch-transport/spec/spec_helper.rb delete mode 100644 elasticsearch-transport/test/integration/jruby_test.rb delete mode 100644 elasticsearch-transport/test/integration/transport_test.rb delete mode 100644 elasticsearch-transport/test/profile/client_benchmark_test.rb delete mode 100644 elasticsearch-transport/test/test_helper.rb delete mode 100644 elasticsearch-transport/test/unit/connection_test.rb delete mode 100644 elasticsearch-transport/test/unit/response_test.rb delete mode 100644 elasticsearch-transport/test/unit/serializer_test.rb delete mode 100644 elasticsearch-transport/test/unit/transport_base_test.rb delete mode 100644 elasticsearch-transport/test/unit/transport_curb_test.rb delete mode 100644 elasticsearch-transport/test/unit/transport_faraday_test.rb delete mode 100644 elasticsearch-transport/test/unit/transport_manticore_test.rb diff --git a/Gemfile b/Gemfile index 3f62929333..c41191c1eb 100644 --- a/Gemfile +++ b/Gemfile @@ -18,7 +18,6 @@ source 'https://rubygems.org' gem 'elasticsearch-api', path: File.expand_path('../elasticsearch-api', __FILE__), require: false -gem 'elasticsearch-transport', path: File.expand_path('../elasticsearch-transport', __FILE__), require: false gem 'elasticsearch', path: File.expand_path('../elasticsearch', __FILE__), require: false gem 'ansi' diff --git a/elasticsearch-transport/.gitignore b/elasticsearch-transport/.gitignore deleted file mode 100644 index d87d4be66f..0000000000 --- a/elasticsearch-transport/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -*.gem -*.rbc -.bundle -.config -.yardoc -Gemfile.lock -InstalledFiles -_yardoc -coverage -doc/ -lib/bundler/man -pkg -rdoc -spec/reports -test/tmp -test/version_tmp -tmp diff --git a/elasticsearch-transport/Gemfile b/elasticsearch-transport/Gemfile deleted file mode 100644 index 7b1b8bdf9b..0000000000 --- a/elasticsearch-transport/Gemfile +++ /dev/null @@ -1,38 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -source 'https://rubygems.org' - -# Specify your gem's dependencies in elasticsearch-transport.gemspec -gemspec - -if File.exist? File.expand_path('../elasticsearch-api/elasticsearch-api.gemspec', __dir__) - gem 'elasticsearch-api', path: File.expand_path('../elasticsearch-api', __dir__), require: false -end - -if File.exist? File.expand_path('../elasticsearch/elasticsearch.gemspec', __dir__) - gem 'elasticsearch', path: File.expand_path('../elasticsearch', __dir__), require: false -end - -group :development, :test do - gem 'rspec' - if defined?(JRUBY_VERSION) - gem 'pry-nav' - else - gem 'pry-byebug' - end -end diff --git a/elasticsearch-transport/LICENSE.txt b/elasticsearch-transport/LICENSE.txt deleted file mode 100644 index d645695673..0000000000 --- a/elasticsearch-transport/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/elasticsearch-transport/README.md b/elasticsearch-transport/README.md deleted file mode 100644 index 7180ef6fba..0000000000 --- a/elasticsearch-transport/README.md +++ /dev/null @@ -1,635 +0,0 @@ -# Elasticsearch::Transport - -**This library is part of the [`elasticsearch-ruby`](https://github.com/elasticsearch/elasticsearch-ruby/) package; please refer to it, unless you want to use this library standalone.** - ----- - -The `elasticsearch-transport` library provides a low-level Ruby client for connecting -to an [Elasticsearch](http://elasticsearch.com) cluster. - -It handles connecting to multiple nodes in the cluster, rotating across connections, -logging and tracing requests and responses, maintaining failed connections, -discovering nodes in the cluster, and provides an abstraction for -data serialization and transport. - -It does not handle calling the Elasticsearch API; -see the [`elasticsearch-api`](https://github.com/elasticsearch/elasticsearch-ruby/tree/main/elasticsearch-api) library. - -The library is compatible with Ruby 1.9 or higher and with all versions of Elasticsearch since 0.90. - -Features overview: - -* Pluggable logging and tracing -* Pluggable connection selection strategies (round-robin, random, custom) -* Pluggable transport implementation, customizable and extendable -* Pluggable serializer implementation -* Request retries and dead connections handling -* Node reloading (based on cluster state) on errors or on demand - -For optimal performance, use a HTTP library which supports persistent ("keep-alive") connections, -such as [patron](https://github.com/toland/patron) or [Typhoeus](https://github.com/typhoeus/typhoeus). -Just require the library (`require 'patron'`) in your code, and it will be automatically used. - -Currently these libraries will be automatically detected and used: -- [Patron](https://github.com/toland/patron) -- [Typhoeus](https://github.com/typhoeus/typhoeus) -- [HTTPClient](https://rubygems.org/gems/httpclient) -- [Net::HTTP::Persistent](https://rubygems.org/gems/net-http-persistent) - -**Note on [Typhoeus](https://github.com/typhoeus/typhoeus)**: You need to use v1.4.0 or up since older versions are not compatible with Faraday 1.0. - -For detailed information, see example configurations [below](#transport-implementations). - -## Installation - -Install the package from [Rubygems](https://rubygems.org): - - gem install elasticsearch-transport - -To use an unreleased version, either add it to your `Gemfile` for [Bundler](http://gembundler.com): - - gem 'elasticsearch-transport', git: 'git://github.com/elasticsearch/elasticsearch-ruby.git' - -or install it from a source code checkout: - -```bash -git clone https://github.com/elasticsearch/elasticsearch-ruby.git -cd elasticsearch-ruby/elasticsearch-transport -bundle install -rake install -``` - -## Example Usage - -In the simplest form, connect to Elasticsearch running on -without any configuration: - -```ruby -require 'elasticsearch/transport' - -client = Elasticsearch::Client.new -response = client.perform_request 'GET', '_cluster/health' -# => # -``` - - -Full documentation is available at . - -## Configuration - -* [Setting Hosts](#setting-hosts) -* [Default port](#default-port) -* [Connect using an Elastic Cloud ID](#connect-using-an-elastic-cloud-id) -* [Authentication](#authentication) -* [Logging](#logging) -* [APM integration](#apm-integration) -* [Custom HTTP Headers](#custom-http-headers) -* [Identifying running tasks with X-Opaque-Id](#identifying-running-tasks-with-x-opaque-id) -* [Setting Timeouts](#setting-timeouts) -* [Randomizing Hosts](#randomizing-hosts) -* [Retrying on Failures](#retrying-on-failures) -* [Reloading Hosts](#reloading-hosts) -* [Connection Selector](#connection-selector) -* [Transport Implementations](#transport-implementations) -* [Serializer implementations](#serializer-implementations) -* [Exception Handling](#exception-handling) -* [Development and Community](#development-and-community) - -The client supports many configurations options for setting up and managing connections, -configuring logging, customizing the transport library, etc. - -### Setting Hosts - -To connect to a specific Elasticsearch host: - -```ruby -Elasticsearch::Client.new host: 'search.myserver.com' -``` - -To connect to a host with specific port: - -```ruby -Elasticsearch::Client.new host: 'myhost:8080' -``` - -To connect to multiple hosts: - -```ruby -Elasticsearch::Client.new hosts: ['myhost1', 'myhost2'] -``` - - -Instead of Strings, you can pass host information as an array of Hashes: - -```ruby -Elasticsearch::Client.new hosts: [ { host: 'myhost1', port: 8080 }, { host: 'myhost2', port: 8080 } ] -``` - -**NOTE:** When specifying multiple hosts, you probably want to enable the `retry_on_failure` or `retry_on_status` options to - perform a failed request on another node (see the _Retrying on Failures_ chapter). - -Common URL parts -- scheme, HTTP authentication credentials, URL prefixes, etc -- are handled automatically: -```ruby -Elasticsearch::Client.new url: 'https://username:password@api.server.org:4430/search' -``` - -You can pass multiple URLs separated by a comma: -```ruby -Elasticsearch::Client.new urls: 'http://localhost:9200,http://localhost:9201' -``` - -Another way to configure the URL(s) is to export the `ELASTICSEARCH_URL` variable. - -The client will automatically round-robin across the hosts -(unless you select or implement a different [connection selector](#connection-selector)). - -### Default port - -The default port is `9200`. Please specify a port for your host(s) if they differ from this default. -Please see below for an exception to this when connecting using an Elastic Cloud ID. - -### Connect using an Elastic Cloud ID - -If you are using [Elastic Cloud](https://www.elastic.co/cloud), you can provide your cloud id to the client. -You must supply your username and password separately, and optionally a port. If no port is supplied, -port 9243 will be used. - -Note: Do not enable sniffing when using Elastic Cloud. The nodes are behind a load balancer so -Elastic Cloud will take care of everything for you. - -```ruby -Elasticsearch::Client.new(cloud_id: 'name:bG9jYWxob3N0JGFiY2QkZWZnaA==', user: 'elastic', password: 'changeme') -``` - -### Authentication - -You can pass the authentication credentials, scheme and port in the host configuration hash: - -```ruby -Elasticsearch::Client.new hosts: [ - { host: 'my-protected-host', - port: '443', - user: 'USERNAME', - password: 'PASSWORD', - scheme: 'https' - } -] -``` -... or simply use the common URL format: - -```ruby -Elasticsearch::Client.new url: 'https://username:password@example.com:9200' -``` - -To pass a custom certificate for SSL peer verification to Faraday-based clients, -use the `transport_options` option: - -```ruby -Elasticsearch::Client.new url: 'https://username:password@example.com:9200', - transport_options: { ssl: { ca_file: '/path/to/cacert.pem' } } -``` - -You can also use [**API Key authentication**](https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-create-api-key.html): - -``` ruby -Elasticsearch::Client.new( - host: host, - transport_options: transport_options, - api_key: credentials -) -``` - -Where credentials is either the base64 encoding of `id` and `api_key` joined by a colon or a hash with the `id` and `api_key`: - -``` ruby -Elasticsearch::Client.new( - host: host, - transport_options: transport_options, - api_key: {id: 'my_id', api_key: 'my_api_key'} -) -``` - -### Logging - -To log requests and responses to standard output with the default logger (an instance of Ruby's {::Logger} class), set the `log` argument to true: - -```ruby -Elasticsearch::Client.new(log: true) -``` - -You can also use [ecs-logging](https://github.com/elastic/ecs-logging-ruby). `ecs-logging` is a set of libraries that allows you to transform your application logs to structured logs that comply with the [Elastic Common Schema (ECS)](https://www.elastic.co/guide/en/ecs/current/ecs-reference.html): - -```ruby -logger = EcsLogging::Logger.new($stdout) -Elasticsearch::Client.new(logger: logger) -``` - -To trace requests and responses in the _Curl_ format, set the `trace` argument: - -```ruby -Elasticsearch::Client.new(trace: true) -``` - -You can customize the default logger or tracer: - -```ruby -client.transport.logger.formatter = proc { |s, d, p, m| "#{s}: #{m}\n" } -client.transport.logger.level = Logger::INFO -``` - -Or, you can use a custom `::Logger` instance: - -```ruby -Elasticsearch::Client.new(logger: Logger.new(STDERR)) -``` - -You can pass the client any conforming logger implementation: - -```ruby -require 'logging' # https://github.com/TwP/logging/ - -log = Logging.logger['elasticsearch'] -log.add_appenders Logging.appenders.stdout -log.level = :info - -client = Elasticsearch::Client.new(logger: log) -``` -### APM integration - -This client integrates seamlessly with Elastic APM via the [Elastic APM Agent](https://github.com/elastic/apm-agent-ruby). It will automatically capture client requests if you are using the agent on your code. If you're using `elastic-apm` v3.8.0 or up, you can set `capture_elasticsearch_queries` to `true` in `config/elastic_apm.yml` to also capture the body from requests in Elasticsearch. See [here](https://github.com/elastic/elasticsearch-ruby/tree/main/docs/examples/apm) for an example. - -### Custom HTTP Headers - -You can set a custom HTTP header on the client's initializer: - -```ruby -client = Elasticsearch::Client.new( - transport_options: { - headers: - {user_agent: "My App"} - } -) -``` - -You can also pass in `headers` as a parameter to any of the API Endpoints to set custom headers for the request: - -```ruby -client.search(index: 'myindex', q: 'title:test', headers: {user_agent: "My App"}) -``` - -### Identifying running tasks with X-Opaque-Id - -The X-Opaque-Id header allows to track certain calls, or associate certain tasks with the client that started them ([more on the Elasticsearch docs](https://www.elastic.co/guide/en/elasticsearch/reference/master/tasks.html#_identifying_running_tasks)). To use this feature, you need to set an id for `opaque_id` on the client on each request. Example: - -```ruby -client = Elasticsearch::Client.new -client.search(index: 'myindex', q: 'title:test', opaque_id: '123456') -``` -The search request will include the following HTTP Header: -``` -X-Opaque-Id: 123456 -``` - -You can also set a prefix for X-Opaque-Id when initializing the client. This will be prepended to the id you set before each request if you're using X-Opaque-Id. Example: -```ruby -client = Elasticsearch::Client.new(opaque_id_prefix: 'eu-west1_') -client.search(index: 'myindex', q: 'title:test', opaque_id: '123456') -``` -The request will include the following HTTP Header: -``` -X-Opaque-Id: eu-west1_123456 -``` - -### Setting Timeouts - -For many operations in Elasticsearch, the default timeouts of HTTP libraries are too low. -To increase the timeout, you can use the `request_timeout` parameter: - -```ruby -Elasticsearch::Client.new request_timeout: 5*60 -``` - -You can also use the `transport_options` argument documented below. - -### Randomizing Hosts - -If you pass multiple hosts to the client, it rotates across them in a round-robin fashion, by default. -When the same client would be running in multiple processes (eg. in a Ruby web server such as Thin), -it might keep connecting to the same nodes "at once". To prevent this, you can randomize the hosts -collection on initialization and reloading: - -```ruby -Elasticsearch::Client.new hosts: ['localhost:9200', 'localhost:9201'], randomize_hosts: true -``` - -### Retrying on Failures - -When the client is initialized with multiple hosts, it makes sense to retry a failed request -on a different host: - -```ruby -Elasticsearch::Client.new hosts: ['localhost:9200', 'localhost:9201'], retry_on_failure: true -``` - -By default, the client will retry the request 3 times. You can specify how many times to retry before it raises an exception by passing a number to `retry_on_failure`: - -```ruby -Elasticsearch::Client.new hosts: ['localhost:9200', 'localhost:9201'], retry_on_failure: 5 -``` - -You can also use `retry_on_status` to retry when specific status codes are returned: - -```ruby -Elasticsearch::Client.new hosts: ['localhost:9200', 'localhost:9201'], retry_on_status: [502, 503] -``` - -These two parameters can also be used together: - -```ruby -Elasticsearch::Client.new hosts: ['localhost:9200', 'localhost:9201'], retry_on_status: [502, 503], retry_on_failure: 10 -``` - -### Reloading Hosts - -Elasticsearch by default dynamically discovers new nodes in the cluster. You can leverage this -in the client, and periodically check for new nodes to spread the load. - -To retrieve and use the information from the -[_Nodes Info API_](http://www.elastic.co/guide/en/elasticsearch/reference/current/cluster-nodes-info.html) -on every 10,000th request: - -```ruby -Elasticsearch::Client.new hosts: ['localhost:9200', 'localhost:9201'], reload_connections: true -``` - -You can pass a specific number of requests after which the reloading should be performed: - -```ruby -Elasticsearch::Client.new hosts: ['localhost:9200', 'localhost:9201'], reload_connections: 1_000 -``` - -To reload connections on failures, use: - -```ruby -Elasticsearch::Client.new hosts: ['localhost:9200', 'localhost:9201'], reload_on_failure: true -``` - -The reloading will timeout if not finished under 1 second by default. To change the setting: - -```ruby -Elasticsearch::Client.new hosts: ['localhost:9200', 'localhost:9201'], sniffer_timeout: 3 -``` - -**NOTE:** When using reloading hosts ("sniffing") together with authentication, just pass the scheme, - user and password with the host info -- or, for more clarity, in the `http` options: - -```ruby -Elasticsearch::Client.new host: 'localhost:9200', - http: { scheme: 'https', user: 'U', password: 'P' }, - reload_connections: true, - reload_on_failure: true -``` - -### Connection Selector - -By default, the client will rotate the connections in a round-robin fashion, using the -{Elasticsearch::Transport::Transport::Connections::Selector::RoundRobin} strategy. - -You can implement your own strategy to customize the behaviour. For example, -let's have a "rack aware" strategy, which will prefer the nodes with a specific -[attribute](https://github.com/elasticsearch/elasticsearch/blob/1.0/config/elasticsearch.yml#L81-L85). -Only when these would be unavailable, the strategy will use the other nodes: - -```ruby -class RackIdSelector - include Elasticsearch::Transport::Transport::Connections::Selector::Base - - def select(options={}) - connections.select do |c| - # Try selecting the nodes with a `rack_id:x1` attribute first - c.host[:attributes] && c.host[:attributes][:rack_id] == 'x1' - end.sample || connections.to_a.sample - end -end - -Elasticsearch::Client.new hosts: ['x1.search.org', 'x2.search.org'], selector_class: RackIdSelector -``` - -### Transport Implementations - -By default, the client will use the [_Faraday_](https://rubygems.org/gems/faraday) HTTP library -as a transport implementation. - -It will auto-detect and use an _adapter_ for _Faraday_ based on gems loaded in your code, -preferring HTTP clients with support for persistent connections. - -To use the [_Patron_](https://github.com/toland/patron) HTTP, for example, just require it: - -```ruby -require 'patron' -``` - -Then, create a new client, and the _Patron_ gem will be used as the "driver": - -```ruby -client = Elasticsearch::Client.new - -client.transport.connections.first.connection.builder.adapter -# => Faraday::Adapter::Patron - -10.times do - client.nodes.stats(metric: 'http')['nodes'].values.each do |n| - puts "#{n['name']} : #{n['http']['total_opened']}" - end -end - -# => Stiletoo : 24 -# => Stiletoo : 24 -# => Stiletoo : 24 -# => ... -``` - -To use a specific adapter for _Faraday_, pass it as the `adapter` argument: - -```ruby -client = Elasticsearch::Client.new adapter: :net_http_persistent - -client.transport.connections.first.connection.builder.handlers -# => [Faraday::Adapter::NetHttpPersistent] -``` - -To pass options to the -[`Faraday::Connection`](https://github.com/lostisland/faraday/blob/master/lib/faraday/connection.rb) -constructor, use the `transport_options` key: - -```ruby -client = Elasticsearch::Client.new transport_options: { - request: { open_timeout: 1 }, - headers: { user_agent: 'MyApp' }, - params: { :format => 'yaml' }, - ssl: { verify: false } -} -``` - -To configure the _Faraday_ instance directly, use a block: - -```ruby -require 'patron' - -client = Elasticsearch::Client.new(host: 'localhost', port: '9200') do |f| - f.response :logger - f.adapter :patron -end -``` - -You can use any standard Faraday middleware and plugins in the configuration block. You can also initialize the transport class yourself, and pass it to the client constructor as the `transport` argument: - -```ruby -require 'patron' - -transport_configuration = lambda do |f| - f.response :logger - f.adapter :patron -end - -transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new \ - hosts: [ { host: 'localhost', port: '9200' } ], - &transport_configuration - -# Pass the transport to the client -# -client = Elasticsearch::Client.new transport: transport -``` - -Instead of passing the transport to the constructor, you can inject it at run time: - -```ruby -# Set up the transport -# -faraday_configuration = lambda do |f| - f.instance_variable_set :@ssl, { verify: false } - f.adapter :excon -end - -faraday_client = Elasticsearch::Transport::Transport::HTTP::Faraday.new \ - hosts: [ { host: 'my-protected-host', - port: '443', - user: 'USERNAME', - password: 'PASSWORD', - scheme: 'https' - }], - &faraday_configuration - -# Create a default client -# -client = Elasticsearch::Client.new - -# Inject the transport to the client -# -client.transport = faraday_client -``` - -You can also use a bundled [_Curb_](https://rubygems.org/gems/curb) based transport implementation: - -```ruby -require 'curb' -require 'elasticsearch/transport/transport/http/curb' - -client = Elasticsearch::Client.new transport_class: Elasticsearch::Transport::Transport::HTTP::Curb - -client.transport.connections.first.connection -# => # -``` - -It's possible to customize the _Curb_ instance by passing a block to the constructor as well -(in this case, as an inline block): - -```ruby -transport = Elasticsearch::Transport::Transport::HTTP::Curb.new \ - hosts: [ { host: 'localhost', port: '9200' } ], - & lambda { |c| c.verbose = true } - -client = Elasticsearch::Client.new transport: transport -``` - -You can write your own transport implementation easily, by including the -{Elasticsearch::Transport::Transport::Base} module, implementing the required contract, -and passing it to the client as the `transport_class` parameter -- or injecting it directly. - -### Serializer Implementations - -By default, the [MultiJSON](http://rubygems.org/gems/multi_json) library is used as the -serializer implementation, and it will pick up the "right" adapter based on gems available. - -The serialization component is pluggable, though, so you can write your own by including the -{Elasticsearch::Transport::Transport::Serializer::Base} module, implementing the required contract, -and passing it to the client as the `serializer_class` or `serializer` parameter. - -### Exception Handling - -The library defines a [number of exception classes](https://github.com/elasticsearch/elasticsearch-ruby/blob/main/elasticsearch-transport/lib/elasticsearch/transport/transport/errors.rb) -for various client and server errors, as well as unsuccessful HTTP responses, -making it possible to `rescue` specific exceptions with desired granularity. - -The highest-level exception is {Elasticsearch::Transport::Transport::Error} -and will be raised for any generic client *or* server errors. - -{Elasticsearch::Transport::Transport::ServerError} will be raised for server errors only. - -As an example for response-specific errors, a `404` response status will raise -an {Elasticsearch::Transport::Transport::Errors::NotFound} exception. - -Finally, {Elasticsearch::Transport::Transport::SnifferTimeoutError} will be raised -when connection reloading ("sniffing") times out. - -## Development and Community - -For local development, clone the repository and run `bundle install`. See `rake -T` for a list of -available Rake tasks for running tests, generating documentation, starting a testing cluster, etc. - -Bug fixes and features must be covered by unit tests. Integration tests are written in Ruby 1.9 syntax. - -Github's pull requests and issues are used to communicate, send bug reports and code contributions. - -## The Architecture - -* {Elasticsearch::Transport::Client} is composed of {Elasticsearch::Transport::Transport} - -* {Elasticsearch::Transport::Transport} is composed of {Elasticsearch::Transport::Transport::Connections}, - and an instance of logger, tracer, serializer and sniffer. - -* Logger and tracer can be any object conforming to Ruby logging interface, - ie. an instance of [`Logger`](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html), - [_log4r_](https://rubygems.org/gems/log4r), [_logging_](https://github.com/TwP/logging/), etc. - -* The {Elasticsearch::Transport::Transport::Serializer::Base} implementations handle converting data for Elasticsearch - (eg. to JSON). You can implement your own serializer. - -* {Elasticsearch::Transport::Transport::Sniffer} allows to discover nodes in the cluster and use them as connections. - -* {Elasticsearch::Transport::Transport::Connections::Collection} is composed of - {Elasticsearch::Transport::Transport::Connections::Connection} instances and a selector instance. - -* {Elasticsearch::Transport::Transport::Connections::Connection} contains the connection attributes such as hostname and port, - as well as the concrete persistent "session" connected to a specific node. - -* The {Elasticsearch::Transport::Transport::Connections::Selector::Base} implementations allow to choose connections - from the pool, eg. in a round-robin or random fashion. You can implement your own selector strategy. - -## Development - -To work on the code, clone and bootstrap the main repository first -- -please see instructions in the main [README](../README.md#development). - -To run tests, launch a testing cluster and use the Rake tasks: - -```bash -time rake test:unit -time rake test:integration -``` - -Use `COVERAGE=true` before running a test task to check coverage with Simplecov. - -## License - -This software is licensed under the [Apache 2 license](./LICENSE). diff --git a/elasticsearch-transport/Rakefile b/elasticsearch-transport/Rakefile deleted file mode 100644 index 42ca4adf7b..0000000000 --- a/elasticsearch-transport/Rakefile +++ /dev/null @@ -1,80 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require "bundler/gem_tasks" - -desc "Run unit tests" -task :default => 'test:unit' -task :test => 'test:unit' - -# ----- Test tasks ------------------------------------------------------------ - -require 'rake/testtask' -require 'rspec/core/rake_task' - -namespace :test do - desc 'Wait for Elasticsearch to be in a green state' - task :wait_for_green do - sh '../scripts/wait-cluster.sh' - end - - task spec: :wait_for_green - RSpec::Core::RakeTask.new(:spec) - - Rake::TestTask.new(:unit) do |test| - test.libs << 'lib' << 'test' - test.test_files = FileList['test/unit/**/*_test.rb'] - test.verbose = false - test.warning = false - end - - Rake::TestTask.new(:integration) do |test| - test.libs << 'lib' << 'test' - test.test_files = FileList['test/integration/**/*_test.rb'] - test.deps = ['test:wait_for_green', 'test:spec'] - test.verbose = false - test.warning = false - end - - desc 'Run all tests' - task :all do - Rake::Task['test:unit'].invoke - Rake::Task['test:integration'].invoke - end - - Rake::TestTask.new(:profile) do |test| - test.libs << 'lib' << 'test' - test.test_files = FileList['test/profile/**/*_test.rb'] - end -end - -# ----- Documentation tasks --------------------------------------------------- - -require 'yard' -YARD::Rake::YardocTask.new(:doc) do |t| - t.options = %w| --embed-mixins --markup=markdown | -end - -# ----- Code analysis tasks --------------------------------------------------- - -if defined?(RUBY_VERSION) && RUBY_VERSION > '1.9' - require 'cane/rake_task' - Cane::RakeTask.new(:quality) do |cane| - cane.abc_max = 15 - cane.no_style = true - end -end diff --git a/elasticsearch-transport/elasticsearch-transport.gemspec b/elasticsearch-transport/elasticsearch-transport.gemspec deleted file mode 100644 index bd4b305fc8..0000000000 --- a/elasticsearch-transport/elasticsearch-transport.gemspec +++ /dev/null @@ -1,75 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# coding: utf-8 -lib = File.expand_path('../lib', __FILE__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'elasticsearch/transport/version' - -Gem::Specification.new do |s| - s.name = "elasticsearch-transport" - s.version = Elasticsearch::Transport::VERSION - s.authors = ['Karel Minarik'] - s.email = ['karel.minarik@elasticsearch.org'] - s.summary = 'Ruby client for Elasticsearch.' - s.homepage = 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/current/index.html' - s.license = 'Apache-2.0' - s.metadata = { - 'homepage_uri' => 'https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/current/index.html', - 'changelog_uri' => 'https://github.com/elastic/elasticsearch-ruby/blob/main/CHANGELOG.md', - 'source_code_uri' => 'https://github.com/elastic/elasticsearch-ruby/tree/main/elasticsearch-transport', - 'bug_tracker_uri' => 'https://github.com/elastic/elasticsearch-ruby/issues' - } - s.files = `git ls-files`.split($/) - s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) } - s.test_files = s.files.grep(%r{^(test|spec|features)/}) - s.require_paths = ['lib'] - - s.extra_rdoc_files = [ 'README.md', 'LICENSE.txt' ] - s.rdoc_options = [ '--charset=UTF-8' ] - - s.required_ruby_version = '>= 2.5' - - s.add_dependency 'multi_json' - s.add_dependency 'faraday', '~> 1' - - s.add_development_dependency 'ansi' - s.add_development_dependency 'bundler' - s.add_development_dependency 'cane' - s.add_development_dependency 'curb' unless defined? JRUBY_VERSION - s.add_development_dependency 'hashie' - s.add_development_dependency 'httpclient' - s.add_development_dependency 'manticore' if defined? JRUBY_VERSION - s.add_development_dependency 'minitest' - s.add_development_dependency 'minitest-reporters' - s.add_development_dependency 'mocha' - s.add_development_dependency 'net-http-persistent' - s.add_development_dependency 'patron' unless defined? JRUBY_VERSION - s.add_development_dependency 'pry' - s.add_development_dependency 'rake', '~> 13' - s.add_development_dependency 'require-prof' unless defined?(JRUBY_VERSION) || defined?(Rubinius) - s.add_development_dependency 'ruby-prof' unless defined?(JRUBY_VERSION) || defined?(Rubinius) - s.add_development_dependency 'shoulda-context' - s.add_development_dependency 'simplecov' - s.add_development_dependency 'test-unit', '~> 2' - s.add_development_dependency 'typhoeus', '~> 1.4' - s.add_development_dependency 'yard' - - s.description = <<-DESC.gsub(/^ /, '') - Ruby client for Elasticsearch. See the `elasticsearch` gem for full integration. - DESC -end diff --git a/elasticsearch-transport/lib/elasticsearch-transport.rb b/elasticsearch-transport/lib/elasticsearch-transport.rb deleted file mode 100644 index a124048f89..0000000000 --- a/elasticsearch-transport/lib/elasticsearch-transport.rb +++ /dev/null @@ -1,18 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'elasticsearch/transport' diff --git a/elasticsearch-transport/lib/elasticsearch/transport.rb b/elasticsearch-transport/lib/elasticsearch/transport.rb deleted file mode 100644 index c142d8228f..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport.rb +++ /dev/null @@ -1,37 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require "uri" -require "time" -require "timeout" -require "multi_json" -require "faraday" - -require "elasticsearch/transport/transport/loggable" -require "elasticsearch/transport/transport/serializer/multi_json" -require "elasticsearch/transport/transport/sniffer" -require "elasticsearch/transport/transport/response" -require "elasticsearch/transport/transport/errors" -require "elasticsearch/transport/transport/base" -require "elasticsearch/transport/transport/connections/selector" -require "elasticsearch/transport/transport/connections/connection" -require "elasticsearch/transport/transport/connections/collection" -require "elasticsearch/transport/transport/http/faraday" -require "elasticsearch/transport/client" -require "elasticsearch/transport/redacted" - -require "elasticsearch/transport/version" diff --git a/elasticsearch-transport/lib/elasticsearch/transport/client.rb b/elasticsearch-transport/lib/elasticsearch/transport/client.rb deleted file mode 100644 index bbaee5e849..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/client.rb +++ /dev/null @@ -1,282 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'base64' -require 'elasticsearch/transport/meta_header' - -module Elasticsearch - module Transport - # Handles communication with an Elasticsearch cluster. - # - # See {file:README.md README} for usage and code examples. - # - class Client - DEFAULT_TRANSPORT_CLASS = Transport::HTTP::Faraday - include MetaHeader - - DEFAULT_LOGGER = lambda do - require 'logger' - logger = Logger.new(STDERR) - logger.progname = 'elasticsearch' - logger.formatter = proc { |severity, datetime, progname, msg| "#{datetime}: #{msg}\n" } - logger - end - - DEFAULT_TRACER = lambda do - require 'logger' - logger = Logger.new(STDERR) - logger.progname = 'elasticsearch.tracer' - logger.formatter = proc { |severity, datetime, progname, msg| "#{msg}\n" } - logger - end - - # The default host and port to use if not otherwise specified. - # - # @since 7.0.0 - DEFAULT_HOST = 'localhost:9200'.freeze - - # The default port to use if not otherwise specified. - # - # @since 7.2.0 - DEFAULT_PORT = 9200 - - # Returns the transport object. - # - # @see Elasticsearch::Transport::Transport::Base - # @see Elasticsearch::Transport::Transport::HTTP::Faraday - # - attr_accessor :transport - - # Create a client connected to an Elasticsearch cluster. - # - # Specify the URL via arguments or set the `ELASTICSEARCH_URL` environment variable. - # - # @option arguments [String,Array] :hosts Single host passed as a String or Hash, or multiple hosts - # passed as an Array; `host` or `url` keys are also valid - # - # @option arguments [Boolean] :log Use the default logger (disabled by default) - # - # @option arguments [Boolean] :trace Use the default tracer (disabled by default) - # - # @option arguments [Object] :logger An instance of a Logger-compatible object - # - # @option arguments [Object] :tracer An instance of a Logger-compatible object - # - # @option arguments [Number] :resurrect_after After how many seconds a dead connection should be tried again - # - # @option arguments [Boolean,Number] :reload_connections Reload connections after X requests (false by default) - # - # @option arguments [Boolean] :randomize_hosts Shuffle connections on initialization and reload (false by default) - # - # @option arguments [Integer] :sniffer_timeout Timeout for reloading connections in seconds (1 by default) - # - # @option arguments [Boolean,Number] :retry_on_failure Retry X times when request fails before raising and - # exception (false by default) - # @option arguments Array :retry_on_status Retry when specific status codes are returned - # - # @option arguments [Boolean] :reload_on_failure Reload connections after failure (false by default) - # - # @option arguments [Integer] :request_timeout The request timeout to be passed to transport in options - # - # @option arguments [Symbol] :adapter A specific adapter for Faraday (e.g. `:patron`) - # - # @option arguments [Hash] :transport_options Options to be passed to the `Faraday::Connection` constructor - # - # @option arguments [Constant] :transport_class A specific transport class to use, will be initialized by - # the client and passed hosts and all arguments - # - # @option arguments [Object] :transport A specific transport instance - # - # @option arguments [Constant] :serializer_class A specific serializer class to use, will be initialized by - # the transport and passed the transport instance - # - # @option arguments [Constant] :selector An instance of selector strategy implemented with - # {Elasticsearch::Transport::Transport::Connections::Selector::Base}. - # - # @option arguments [String] :send_get_body_as Specify the HTTP method to use for GET requests with a body. - # (Default: GET) - # @option arguments [true, false] :compression Whether to compress requests. Gzip compression will be used. - # The default is false. Responses will automatically be inflated if they are compressed. - # If a custom transport object is used, it must handle the request compression and response inflation. - # - # @option enable_meta_header [Boolean] :enable_meta_header Enable sending the meta data header to Cloud. - # (Default: true) - # - # @yield [faraday] Access and configure the `Faraday::Connection` instance directly with a block - # - def initialize(arguments = {}, &block) - @options = arguments.transform_keys(&:to_sym) - @arguments = @options - @arguments[:logger] ||= @arguments[:log] ? DEFAULT_LOGGER.call() : nil - @arguments[:tracer] ||= @arguments[:trace] ? DEFAULT_TRACER.call() : nil - @arguments[:reload_connections] ||= false - @arguments[:retry_on_failure] ||= false - @arguments[:reload_on_failure] ||= false - @arguments[:randomize_hosts] ||= false - @arguments[:transport_options] ||= {} - @arguments[:http] ||= {} - @arguments[:enable_meta_header] = arguments.fetch(:enable_meta_header, true) - @options[:http] ||= {} - - @hosts = __extract_hosts(@arguments[:hosts] || - @arguments[:host] || - @arguments[:url] || - @arguments[:urls] || - ENV['ELASTICSEARCH_URL'] || - DEFAULT_HOST) - - @send_get_body_as = @arguments[:send_get_body_as] || 'GET' - - if @arguments[:request_timeout] - @arguments[:transport_options][:request] = { timeout: @arguments[:request_timeout] } - end - - if @arguments[:transport] - @transport = @arguments[:transport] - else - @transport_class = @arguments[:transport_class] || DEFAULT_TRANSPORT_CLASS - @transport = if @transport_class == Transport::HTTP::Faraday - @arguments[:adapter] ||= __auto_detect_adapter - set_meta_header # from include MetaHeader - @transport_class.new(hosts: @hosts, options: @arguments) do |faraday| - faraday.adapter(@arguments[:adapter]) - block&.call faraday - end - else - set_meta_header # from include MetaHeader - @transport_class.new(hosts: @hosts, options: @arguments) - end - end - end - # Performs a request through delegation to {#transport}. - # - def perform_request(method, path, params = {}, body = nil, headers = nil) - method = @send_get_body_as if 'GET' == method && body - transport.perform_request(method, path, params, body, headers) - end - - private - - def add_header(header) - headers = @arguments[:transport_options]&.[](:headers) || {} - headers.merge!(header) - @arguments[:transport_options].merge!( - headers: headers - ) - end - - # Normalizes and returns hosts configuration. - # - # Arrayifies the `hosts_config` argument and extracts `host` and `port` info from strings. - # Performs shuffling when the `randomize_hosts` option is set. - # - # TODO: Refactor, so it's available in Elasticsearch::Transport::Base as well - # - # @return [Array] - # @raise [ArgumentError] - # - # @api private - # - def __extract_hosts(hosts_config) - hosts = case hosts_config - when String - hosts_config.split(',').map { |h| h.strip! || h } - when Array - hosts_config - when Hash, URI - [hosts_config] - else - Array(hosts_config) - end - - host_list = hosts.map { |host| __parse_host(host) } - @options[:randomize_hosts] ? host_list.shuffle! : host_list - end - - def __parse_host(host) - host_parts = case host - when String - if host =~ /^[a-z]+\:\/\// - # Construct a new `URI::Generic` directly from the array returned by URI::split. - # This avoids `URI::HTTP` and `URI::HTTPS`, which supply default ports. - uri = URI::Generic.new(*URI.split(host)) - - default_port = uri.scheme == 'https' ? 443 : DEFAULT_PORT - - { :scheme => uri.scheme, - :user => uri.user, - :password => uri.password, - :host => uri.host, - :path => uri.path, - :port => uri.port || default_port } - else - host, port = host.split(':') - { :host => host, - :port => port } - end - when URI - { :scheme => host.scheme, - :user => host.user, - :password => host.password, - :host => host.host, - :path => host.path, - :port => host.port } - when Hash - host - else - raise ArgumentError, "Please pass host as a String, URI or Hash -- #{host.class} given." - end - - if @api_key - # Remove Basic Auth if using API KEY - host_parts.delete(:user) - host_parts.delete(:password) - else - @options[:http][:user] ||= host_parts[:user] - @options[:http][:password] ||= host_parts[:password] - end - - host_parts[:port] = host_parts[:port].to_i if host_parts[:port] - host_parts[:path].chomp!('/') if host_parts[:path] - host_parts - end - - # Auto-detect the best adapter (HTTP "driver") available, based on libraries - # loaded by the user, preferring those with persistent connections - # ("keep-alive") by default - # - # @return [Symbol] - # - # @api private - # - def __auto_detect_adapter - case - when defined?(::Patron) - :patron - when defined?(::Typhoeus) - :typhoeus - when defined?(::HTTPClient) - :httpclient - when defined?(::Net::HTTP::Persistent) - :net_http_persistent - else - ::Faraday.default_adapter - end - end - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/meta_header.rb b/elasticsearch-transport/lib/elasticsearch/transport/meta_header.rb deleted file mode 100644 index cd4d39be7a..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/meta_header.rb +++ /dev/null @@ -1,135 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'base64' - -module Elasticsearch - module Transport - # Methods for the Elastic meta header used by Cloud. - # X-Elastic-Client-Meta HTTP header which is used by Elastic Cloud and can be disabled when - # instantiating the Client with the :enable_meta_header parameter set to `false`. - # - module MetaHeader - def set_meta_header - return if @arguments[:enable_meta_header] == false - - service, version = meta_header_service_version - - meta_headers = { - service.to_sym => version, - rb: RUBY_VERSION, - t: Elasticsearch::Transport::VERSION - } - meta_headers.merge!(meta_header_engine) if meta_header_engine - meta_headers.merge!(meta_header_adapter) if meta_header_adapter - - add_header({ 'x-elastic-client-meta' => meta_headers.map { |k, v| "#{k}=#{v}" }.join(',') }) - end - - def meta_header_service_version - if enterprise_search? - Elastic::ENTERPRISE_SERVICE_VERSION - elsif elasticsearch? - Elastic::ELASTICSEARCH_SERVICE_VERSION - elsif defined?(Elasticsearch::VERSION) - [:es, client_meta_version(Elasticsearch::VERSION)] - else - [:es, client_meta_version(Elasticsearch::Transport::VERSION)] - end - end - - def enterprise_search? - defined?(Elastic::ENTERPRISE_SERVICE_VERSION) && - called_from?('enterprise-search-ruby') - end - - def elasticsearch? - defined?(Elastic::ELASTICSEARCH_SERVICE_VERSION) && - called_from?('elasticsearch') - end - - def called_from?(service) - !caller.select { |c| c.match?(service) }.empty? - end - - # We return the current version if it's a release, but if it's a pre/alpha/beta release we - # return p - # - def client_meta_version(version) - regexp = /^([0-9]+\.[0-9]+\.[0-9]+)(\.?[a-z0-9.-]+)?$/ - match = version.match(regexp) - return "#{match[1]}p" if (match[2]) - - version - end - - def meta_header_engine - case RUBY_ENGINE - when 'ruby' - {} - when 'jruby' - { jv: ENV_JAVA['java.version'], jr: JRUBY_VERSION } - when 'rbx' - { rbx: RUBY_VERSION } - else - { RUBY_ENGINE.to_sym => RUBY_VERSION } - end - end - - # This function tries to define the version for the Faraday adapter. If it hasn't been loaded - # by the time we're calling this method, it's going to report the adapter (if we know it) but - # return 0 as the version. It won't report anything when using a custom adapter we don't - # identify. - # - # Returns a Hash - # - def meta_header_adapter - if @transport_class == Transport::HTTP::Faraday - version = '0' - adapter_version = case @arguments[:adapter] - when :patron - version = Patron::VERSION if defined?(::Patron::VERSION) - {pt: version} - when :net_http - version = if defined?(Net::HTTP::VERSION) - Net::HTTP::VERSION - elsif defined?(Net::HTTP::HTTPVersion) - Net::HTTP::HTTPVersion - end - {nh: version} - when :typhoeus - version = Typhoeus::VERSION if defined?(::Typhoeus::VERSION) - {ty: version} - when :httpclient - version = HTTPClient::VERSION if defined?(HTTPClient::VERSION) - {hc: version} - when :net_http_persistent - version = Net::HTTP::Persistent::VERSION if defined?(Net::HTTP::Persistent::VERSION) - {np: version} - else - {} - end - {fd: Faraday::VERSION}.merge(adapter_version) - elsif defined?(Transport::HTTP::Curb) && @transport_class == Transport::HTTP::Curb - {cl: Curl::CURB_VERSION} - elsif defined?(Transport::HTTP::Manticore) && @transport_class == Transport::HTTP::Manticore - {mc: Manticore::VERSION} - end - end - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/redacted.rb b/elasticsearch-transport/lib/elasticsearch/transport/redacted.rb deleted file mode 100644 index c300838a09..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/redacted.rb +++ /dev/null @@ -1,75 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Transport - - # Class for wrapping a hash that could have sensitive data. - # When printed, the sensitive values will be redacted. - # - # @since 6.1.1 - class Redacted < ::Hash - - def initialize(elements = nil) - super() - (elements || {}).each_pair{ |key, value| self[key] = value } - end - - # The keys whose values will be redacted. - # - # @since 6.1.1 - SENSITIVE_KEYS = [ :password, - :pwd ].freeze - - # The replacement string used in place of the value for sensitive keys. - # - # @since 6.1.1 - STRING_REPLACEMENT = ''.freeze - - # Get a string representation of the hash. - # - # @return [ String ] The string representation of the hash. - # - # @since 6.1.1 - def inspect - redacted_string(:inspect) - end - - # Get a string representation of the hash. - # - # @return [ String ] The string representation of the hash. - # - # @since 6.1.1 - def to_s - redacted_string(:to_s) - end - - private - - def redacted_string(method) - '{' + reduce([]) do |list, (k, v)| - list << "#{k.send(method)}=>#{redact(k, v, method)}" - end.join(', ') + '}' - end - - def redact(k, v, method) - return STRING_REPLACEMENT if SENSITIVE_KEYS.include?(k.to_sym) - v.send(method) - end - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/base.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/base.rb deleted file mode 100644 index 86cf83c647..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/base.rb +++ /dev/null @@ -1,450 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Transport - module Transport - - # @abstract Module with common functionality for transport implementations. - # - module Base - include Loggable - - DEFAULT_PORT = 9200 - DEFAULT_PROTOCOL = 'http' - DEFAULT_RELOAD_AFTER = 10_000 # Requests - DEFAULT_RESURRECT_AFTER = 60 # Seconds - DEFAULT_MAX_RETRIES = 3 # Requests - DEFAULT_SERIALIZER_CLASS = Serializer::MultiJson - SANITIZED_PASSWORD = '*' * (rand(14)+1) - - attr_reader :hosts, :options, :connections, :counter, :last_request_at, :protocol - attr_accessor :serializer, :sniffer, :logger, :tracer, - :reload_connections, :reload_after, - :resurrect_after - - # Creates a new transport object - # - # @param arguments [Hash] Settings and options for the transport - # @param block [Proc] Lambda or Proc which can be evaluated in the context of the "session" object - # - # @option arguments [Array] :hosts An Array of normalized hosts information - # @option arguments [Array] :options A Hash with options (usually passed by {Client}) - # - # @see Client#initialize - # - def initialize(arguments = {}, &block) - @state_mutex = Mutex.new - - @hosts = arguments[:hosts] || [] - @options = arguments[:options] || {} - @options[:http] ||= {} - @options[:retry_on_status] ||= [] - - @block = block - @compression = !!@options[:compression] - @connections = __build_connections - - @serializer = options[:serializer] || ( options[:serializer_class] ? options[:serializer_class].new(self) : DEFAULT_SERIALIZER_CLASS.new(self) ) - @protocol = options[:protocol] || DEFAULT_PROTOCOL - - @logger = options[:logger] - @tracer = options[:tracer] - - @sniffer = options[:sniffer_class] ? options[:sniffer_class].new(self) : Sniffer.new(self) - @counter = 0 - @counter_mtx = Mutex.new - @last_request_at = Time.now - @reload_connections = options[:reload_connections] - @reload_after = options[:reload_connections].is_a?(Integer) ? options[:reload_connections] : DEFAULT_RELOAD_AFTER - @resurrect_after = options[:resurrect_after] || DEFAULT_RESURRECT_AFTER - @retry_on_status = Array(options[:retry_on_status]).map { |d| d.to_i } - end - - # Returns a connection from the connection pool by delegating to {Connections::Collection#get_connection}. - # - # Resurrects dead connection if the `resurrect_after` timeout has passed. - # Increments the counter and performs connection reloading if the `reload_connections` option is set. - # - # @return [Connections::Connection] - # @see Connections::Collection#get_connection - # - def get_connection(options={}) - resurrect_dead_connections! if Time.now > @last_request_at + @resurrect_after - - @counter_mtx.synchronize { @counter += 1 } - reload_connections! if reload_connections && counter % reload_after == 0 - connections.get_connection(options) - end - - # Reloads and replaces the connection collection based on cluster information - # - # @see Sniffer#hosts - # - def reload_connections! - hosts = sniffer.hosts - __rebuild_connections :hosts => hosts, :options => options - self - rescue SnifferTimeoutError - log_error "[SnifferTimeoutError] Timeout when reloading connections." - self - end - - # Tries to "resurrect" all eligible dead connections - # - # @see Connections::Connection#resurrect! - # - def resurrect_dead_connections! - connections.dead.each { |c| c.resurrect! } - end - - # Rebuilds the connections collection in the transport. - # - # The methods *adds* new connections from the passed hosts to the collection, - # and *removes* all connections not contained in the passed hosts. - # - # @return [Connections::Collection] - # @api private - # - def __rebuild_connections(arguments={}) - @state_mutex.synchronize do - @hosts = arguments[:hosts] || [] - @options = arguments[:options] || {} - - __close_connections - - new_connections = __build_connections - stale_connections = @connections.all.select { |c| ! new_connections.include?(c) } - new_connections = new_connections.reject { |c| @connections.all.include?(c) } - - @connections.remove(stale_connections) - @connections.add(new_connections) - @connections - end - end - - # Builds and returns a collection of connections - # - # The adapters have to implement the {Base#__build_connection} method. - # - # @return [Connections::Collection] - # @api private - # - def __build_connections - Connections::Collection.new( - connections: __connections_from_host, - selector_class: options[:selector_class], - selector: options[:selector] - ) - end - - # Helper function: Maps over hosts, sets protocol, port and user/password and builds connections - # - # @return [Array - # @api private - # - def __connections_from_host - hosts.map do |host| - host[:protocol] = host[:scheme] || options[:scheme] || options[:http][:scheme] || DEFAULT_PROTOCOL - host[:port] ||= options[:port] || options[:http][:port] || DEFAULT_PORT - if (options[:user] || options[:http][:user]) && !host[:user] - host[:user] ||= options[:user] || options[:http][:user] - host[:password] ||= options[:password] || options[:http][:password] - end - - __build_connection(host, (options[:transport_options] || {}), @block) - end - end - - # @abstract Build and return a connection. - # A transport implementation *must* implement this method. - # See {HTTP::Faraday#__build_connection} for an example. - # - # @return [Connections::Connection] - # @api private - # - def __build_connection(host, options={}, block=nil) - raise NoMethodError, "Implement this method in your class" - end - - # Closes the connections collection - # - # @api private - # - def __close_connections - # A hook point for specific adapters when they need to close connections - end - - # Log request and response information - # - # @api private - # - def __log_response(method, path, params, body, url, response, json, took, duration) - if logger - sanitized_url = url.to_s.gsub(/\/\/(.+):(.+)@/, '//' + '\1:' + SANITIZED_PASSWORD + '@') - log_info "#{method.to_s.upcase} #{sanitized_url} " + - "[status:#{response.status}, request:#{sprintf('%.3fs', duration)}, query:#{took}]" - log_debug "> #{__convert_to_json(body)}" if body - log_debug "< #{response.body}" - end - end - - # Trace the request in the `curl` format - # - # @api private - # - def __trace(method, path, params, headers, body, url, response, json, took, duration) - trace_url = "http://localhost:9200/#{path}?pretty" + - ( params.empty? ? '' : "&#{::Faraday::Utils::ParamsHash[params].to_query}" ) - trace_body = body ? " -d '#{__convert_to_json(body, :pretty => true)}'" : '' - trace_command = "curl -X #{method.to_s.upcase}" - trace_command += " -H '#{headers.collect { |k,v| "#{k}: #{v}" }.join(", ")}'" if headers && !headers.empty? - trace_command += " '#{trace_url}'#{trace_body}\n" - tracer.info trace_command - tracer.debug "# #{Time.now.iso8601} [#{response.status}] (#{format('%.3f', duration)}s)\n#" - tracer.debug json ? serializer.dump(json, :pretty => true).gsub(/^/, '# ').sub(/\}$/, "\n# }")+"\n" : "# #{response.body}\n" - end - - # Raise error specific for the HTTP response status or a generic server error - # - # @api private - # - def __raise_transport_error(response) - error = ERRORS[response.status] || ServerError - raise error.new "[#{response.status}] #{response.body}" - end - - # Converts any non-String object to JSON - # - # @api private - # - def __convert_to_json(o=nil, options={}) - o = o.is_a?(String) ? o : serializer.dump(o, options) - end - - # Returns a full URL based on information from host - # - # @param host [Hash] Host configuration passed in from {Client} - # - # @api private - def __full_url(host) - url = "#{host[:protocol]}://" - url += "#{CGI.escape(host[:user])}:#{CGI.escape(host[:password])}@" if host[:user] - url += host[:host] - url += ":#{host[:port]}" if host[:port] - url += host[:path] if host[:path] - url - end - - # Performs a request to Elasticsearch, while handling logging, tracing, marking dead connections, - # retrying the request and reloading the connections. - # - # @abstract The transport implementation has to implement this method either in full, - # or by invoking this method with a block. See {HTTP::Faraday#perform_request} for an example. - # - # @param method [String] Request method - # @param path [String] The API endpoint - # @param params [Hash] Request parameters (will be serialized by {Connections::Connection#full_url}) - # @param body [Hash] Request body (will be serialized by the {#serializer}) - # @param headers [Hash] Request headers (will be serialized by the {#serializer}) - # @param block [Proc] Code block to evaluate, passed from the implementation - # - # @return [Response] - # @raise [NoMethodError] If no block is passed - # @raise [ServerError] If request failed on server - # @raise [Error] If no connection is available - # - def perform_request(method, path, params = {}, body = nil, headers = nil, opts = {}, &block) - raise NoMethodError, 'Implement this method in your transport class' unless block_given? - - start = Time.now - tries = 0 - reload_on_failure = opts.fetch(:reload_on_failure, @options[:reload_on_failure]) - - max_retries = if opts.key?(:retry_on_failure) - opts[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : opts[:retry_on_failure] - elsif options.key?(:retry_on_failure) - options[:retry_on_failure] === true ? DEFAULT_MAX_RETRIES : options[:retry_on_failure] - end - - params = params.clone - ignore = Array(params.delete(:ignore)).compact.map { |s| s.to_i } - - begin - tries += 1 - connection = get_connection or raise Error.new('Cannot get new connection from pool.') - - if connection.connection.respond_to?(:params) && connection.connection.params.respond_to?(:to_hash) - params = connection.connection.params.merge(params.to_hash) - end - - url = connection.full_url(path, params) - response = block.call(connection, url) - connection.healthy! if connection.failures > 0 - - # Raise an exception so we can catch it for `retry_on_status` - __raise_transport_error(response) if response.status.to_i >= 300 && @retry_on_status.include?(response.status.to_i) - - rescue Elasticsearch::Transport::Transport::ServerError => e - if response && @retry_on_status.include?(response.status) - log_warn "[#{e.class}] Attempt #{tries} to get response from #{url}" - if tries <= (max_retries || DEFAULT_MAX_RETRIES) - retry - else - log_fatal "[#{e.class}] Cannot get response from #{url} after #{tries} tries" - raise e - end - else - raise e - end - - rescue *host_unreachable_exceptions => e - log_error "[#{e.class}] #{e.message} #{connection.host.inspect}" - - connection.dead! - - if reload_on_failure and tries < connections.all.size - log_warn "[#{e.class}] Reloading connections (attempt #{tries} of #{connections.all.size})" - reload_connections! and retry - end - - if max_retries - log_warn "[#{e.class}] Attempt #{tries} connecting to #{connection.host.inspect}" - if tries <= max_retries - retry - else - log_fatal "[#{e.class}] Cannot connect to #{connection.host.inspect} after #{tries} tries" - raise e - end - else - raise e - end - - rescue Exception => e - log_fatal "[#{e.class}] #{e.message} (#{connection.host.inspect if connection})" - raise e - - end #/begin - - duration = Time.now - start - - if response.status.to_i >= 300 - __log_response(method, path, params, body, url, response, nil, 'N/A', duration) - __trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer - # Log the failure only when `ignore` doesn't match the response status - log_fatal "[#{response.status}] #{response.body}" unless ignore.include?(response.status.to_i) - __raise_transport_error response unless ignore.include?(response.status.to_i) - end - - json = serializer.load(response.body) if response.body && !response.body.empty? && response.headers && response.headers["content-type"] =~ /json/ - took = (json['took'] ? sprintf('%.3fs', json['took']/1000.0) : 'n/a') rescue 'n/a' - - unless ignore.include?(response.status.to_i) - __log_response method, path, params, body, url, response, json, took, duration - end - - __trace(method, path, params, connection_headers(connection), body, url, response, nil, 'N/A', duration) if tracer - warnings(response.headers['warning']) if response.headers&.[]('warning') - Response.new response.status, json || response.body, response.headers - ensure - @last_request_at = Time.now - end - - # @abstract Returns an Array of connection errors specific to the transport implementation. - # See {HTTP::Faraday#host_unreachable_exceptions} for an example. - # - # @return [Array] - # - def host_unreachable_exceptions - [Errno::ECONNREFUSED] - end - - private - - USER_AGENT_STR = 'User-Agent'.freeze - USER_AGENT_REGEX = /user\-?\_?agent/ - CONTENT_TYPE_STR = 'Content-Type'.freeze - CONTENT_TYPE_REGEX = /content\-?\_?type/ - DEFAULT_CONTENT_TYPE = 'application/json'.freeze - GZIP = 'gzip'.freeze - ACCEPT_ENCODING = 'Accept-Encoding'.freeze - GZIP_FIRST_TWO_BYTES = '1f8b'.freeze - HEX_STRING_DIRECTIVE = 'H*'.freeze - RUBY_ENCODING = '1.9'.respond_to?(:force_encoding) - - def decompress_response(body) - return body unless use_compression? - return body unless gzipped?(body) - - io = StringIO.new(body) - gzip_reader = if RUBY_ENCODING - Zlib::GzipReader.new(io, :encoding => 'ASCII-8BIT') - else - Zlib::GzipReader.new(io) - end - gzip_reader.read - end - - def gzipped?(body) - body[0..1].unpack(HEX_STRING_DIRECTIVE)[0] == GZIP_FIRST_TWO_BYTES - end - - def use_compression? - @compression - end - - def apply_headers(client, options) - headers = options[:headers] || {} - headers[CONTENT_TYPE_STR] = find_value(headers, CONTENT_TYPE_REGEX) || DEFAULT_CONTENT_TYPE - headers[USER_AGENT_STR] = find_value(headers, USER_AGENT_REGEX) || user_agent_header(client) - client.headers[ACCEPT_ENCODING] = GZIP if use_compression? - client.headers.merge!(headers) - end - - def find_value(hash, regex) - key_value = hash.find { |k,v| k.to_s.downcase =~ regex } - if key_value - hash.delete(key_value[0]) - key_value[1] - end - end - - def user_agent_header(client) - @user_agent ||= begin - meta = ["RUBY_VERSION: #{RUBY_VERSION}"] - if RbConfig::CONFIG && RbConfig::CONFIG['host_os'] - meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}" - end - "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})" - end - end - - def warnings(warning) - warn("warning: #{warning}") - end - - def connection_headers(connection) - if defined?(Elasticsearch::Transport::Transport::HTTP::Manticore) && self.class == Elasticsearch::Transport::Transport::HTTP::Manticore - @request_options[:headers] - else - connection.connection.headers - end - end - end - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/connections/collection.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/connections/collection.rb deleted file mode 100644 index f4373e5c6c..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/connections/collection.rb +++ /dev/null @@ -1,127 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Transport - module Transport - module Connections - - # Wraps the collection of connections for the transport object as an Enumerable object. - # - # @see Base#connections - # @see Selector::Base#select - # @see Connection - # - class Collection - include Enumerable - - DEFAULT_SELECTOR = Selector::RoundRobin - - attr_reader :selector - - # @option arguments [Array] :connections An array of {Connection} objects. - # @option arguments [Constant] :selector_class The class to be used as a connection selector strategy. - # @option arguments [Object] :selector The selector strategy object. - # - def initialize(arguments={}) - selector_class = arguments[:selector_class] || DEFAULT_SELECTOR - @connections = arguments[:connections] || [] - @selector = arguments[:selector] || selector_class.new(arguments.merge(:connections => self)) - end - - # Returns an Array of hosts information in this collection as Hashes. - # - # @return [Array] - # - def hosts - @connections.to_a.map { |c| c.host } - end - - # Returns an Array of alive connections. - # - # @return [Array] - # - def connections - @connections.reject { |c| c.dead? } - end - alias :alive :connections - - # Returns an Array of dead connections. - # - # @return [Array] - # - def dead - @connections.select { |c| c.dead? } - end - - # Returns an Array of all connections, both dead and alive - # - # @return [Array] - # - def all - @connections - end - - # Returns a connection. - # - # If there are no alive connections, returns a connection with least failures. - # Delegates to selector's `#select` method to get the connection. - # - # @return [Connection] - # - def get_connection(options={}) - selector.select(options) || @connections.min_by(&:failures) - end - - def each(&block) - connections.each(&block) - end - - def slice(*args) - connections.slice(*args) - end - alias :[] :slice - - def size - connections.size - end - - # Add connection(s) to the collection - # - # @param connections [Connection,Array] A connection or an array of connections to add - # @return [self] - # - def add(connections) - @connections += Array(connections).to_a - self - end - - # Remove connection(s) from the collection - # - # @param connections [Connection,Array] A connection or an array of connections to remove - # @return [self] - # - def remove(connections) - @connections -= Array(connections).to_a - self - end - end - - end - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/connections/connection.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/connections/connection.rb deleted file mode 100644 index 5cfbe09a2a..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/connections/connection.rb +++ /dev/null @@ -1,160 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Transport - module Transport - module Connections - # Wraps the connection information and logic. - # - # The Connection instance wraps the host information (hostname, port, attributes, etc), - # as well as the "session" (a transport client object, such as a {HTTP::Faraday} instance). - # - # It provides methods to construct and properly encode the URLs and paths for passing them - # to the transport client object. - # - # It provides methods to handle connection livecycle (dead, alive, healthy). - # - class Connection - DEFAULT_RESURRECT_TIMEOUT = 60 - - attr_reader :host, :connection, :options, :failures, :dead_since - - # @option arguments [Hash] :host Host information (example: `{host: 'localhost', port: 9200}`) - # @option arguments [Object] :connection The transport-specific physical connection or "session" - # @option arguments [Hash] :options Options (usually passed in from transport) - # - def initialize(arguments={}) - @host = arguments[:host].is_a?(Hash) ? Redacted.new(arguments[:host]) : arguments[:host] - @connection = arguments[:connection] - @options = arguments[:options] || {} - @state_mutex = Mutex.new - - @options[:resurrect_timeout] ||= DEFAULT_RESURRECT_TIMEOUT - @dead = false - @failures = 0 - end - - # Returns the complete endpoint URL with host, port, path and serialized parameters. - # - # @return [String] - # - def full_url(path, params = {}) - url = "#{host[:protocol]}://" - url += "#{CGI.escape(host[:user])}:#{CGI.escape(host[:password])}@" if host[:user] - url += "#{host[:host]}:#{host[:port]}" - url += "#{host[:path]}" if host[:path] - full_path = full_path(path, params) - url += '/' unless full_path.match?(/^\//) - url += full_path - end - - # Returns the complete endpoint path with serialized parameters. - # - # @return [String] - # - def full_path(path, params={}) - path + (params.empty? ? '' : "?#{::Faraday::Utils::ParamsHash[params].to_query}") - end - - # Returns true when this connection has been marked dead, false otherwise. - # - # @return [Boolean] - # - def dead? - @dead || false - end - - # Marks this connection as dead, incrementing the `failures` counter and - # storing the current time as `dead_since`. - # - # @return [self] - # - def dead! - @state_mutex.synchronize do - @dead = true - @failures += 1 - @dead_since = Time.now - end - self - end - - # Marks this connection as alive, ie. it is eligible to be returned from the pool by the selector. - # - # @return [self] - # - def alive! - @state_mutex.synchronize do - @dead = false - end - self - end - - # Marks this connection as healthy, ie. a request has been successfully performed with it. - # - # @return [self] - # - def healthy! - @state_mutex.synchronize do - @dead = false - @failures = 0 - end - self - end - - # Marks this connection as alive, if the required timeout has passed. - # - # @return [self,nil] - # @see DEFAULT_RESURRECT_TIMEOUT - # @see #resurrectable? - # - def resurrect! - alive! if resurrectable? - end - - # Returns true if the connection is eligible to be resurrected as alive, false otherwise. - # - # @return [Boolean] - # - def resurrectable? - @state_mutex.synchronize { - Time.now > @dead_since + ( @options[:resurrect_timeout] * 2 ** (@failures-1) ) - } - end - - # Equality operator based on connection protocol, host, port and attributes - # - # @return [Boolean] - # - def ==(other) - self.host[:protocol] == other.host[:protocol] && \ - self.host[:host] == other.host[:host] && \ - self.host[:port].to_i == other.host[:port].to_i && \ - self.host[:attributes] == other.host[:attributes] - end - - # @return [String] - # - def to_s - "<#{self.class.name} host: #{host} (#{dead? ? 'dead since ' + dead_since.to_s : 'alive'})>" - end - end - - end - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/connections/selector.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/connections/selector.rb deleted file mode 100644 index 57fb6b2937..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/connections/selector.rb +++ /dev/null @@ -1,92 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Transport - module Transport - module Connections - module Selector - - # @abstract Common functionality for connection selector implementations. - # - module Base - attr_reader :connections - - # @option arguments [Connections::Collection] :connections Collection with connections. - # - def initialize(arguments={}) - @connections = arguments[:connections] - end - - # @abstract Selector strategies implement this method to - # select and return a connection from the pool. - # - # @return [Connection] - # - def select(options={}) - raise NoMethodError, "Implement this method in the selector implementation." - end - end - - # "Random connection" selector strategy. - # - class Random - include Base - - # Returns a random connection from the collection. - # - # @return [Connections::Connection] - # - def select(options={}) - connections.to_a.sample - end - end - - # "Round-robin" selector strategy (default). - # - class RoundRobin - include Base - - # @option arguments [Connections::Collection] :connections Collection with connections. - # - def initialize(arguments = {}) - super - @mutex = Mutex.new - @current = nil - end - - # Returns the next connection from the collection, rotating them in round-robin fashion. - # - # @return [Connections::Connection] - # - def select(options={}) - @mutex.synchronize do - conns = connections - if @current && (@current < conns.size-1) - @current += 1 - else - @current = 0 - end - conns[@current] - end - end - end - end - end - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/errors.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/errors.rb deleted file mode 100644 index 24c1dbadde..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/errors.rb +++ /dev/null @@ -1,91 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Transport - module Transport - - # Generic client error - # - class Error < StandardError; end - - # Reloading connections timeout (1 sec by default) - # - class SnifferTimeoutError < Timeout::Error; end - - # Elasticsearch server error (HTTP status 5xx) - # - class ServerError < Error; end - - module Errors; end - - HTTP_STATUSES = { - 300 => 'MultipleChoices', - 301 => 'MovedPermanently', - 302 => 'Found', - 303 => 'SeeOther', - 304 => 'NotModified', - 305 => 'UseProxy', - 307 => 'TemporaryRedirect', - 308 => 'PermanentRedirect', - - 400 => 'BadRequest', - 401 => 'Unauthorized', - 402 => 'PaymentRequired', - 403 => 'Forbidden', - 404 => 'NotFound', - 405 => 'MethodNotAllowed', - 406 => 'NotAcceptable', - 407 => 'ProxyAuthenticationRequired', - 408 => 'RequestTimeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'LengthRequired', - 412 => 'PreconditionFailed', - 413 => 'RequestEntityTooLarge', - 414 => 'RequestURITooLong', - 415 => 'UnsupportedMediaType', - 416 => 'RequestedRangeNotSatisfiable', - 417 => 'ExpectationFailed', - 418 => 'ImATeapot', - 421 => 'TooManyConnectionsFromThisIP', - 426 => 'UpgradeRequired', - 429 => 'TooManyRequests', - 450 => 'BlockedByWindowsParentalControls', - 494 => 'RequestHeaderTooLarge', - 497 => 'HTTPToHTTPS', - 499 => 'ClientClosedRequest', - - 500 => 'InternalServerError', - 501 => 'NotImplemented', - 502 => 'BadGateway', - 503 => 'ServiceUnavailable', - 504 => 'GatewayTimeout', - 505 => 'HTTPVersionNotSupported', - 506 => 'VariantAlsoNegotiates', - 510 => 'NotExtended' - } - - ERRORS = HTTP_STATUSES.inject({}) do |sum, error| - status, name = error - sum[status] = Errors.const_set name, Class.new(ServerError) - sum - end - - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/http/curb.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/http/curb.rb deleted file mode 100644 index 9c41959ff6..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/http/curb.rb +++ /dev/null @@ -1,121 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Transport - module Transport - module HTTP - - # Alternative HTTP transport implementation, using the [_Curb_](https://rubygems.org/gems/curb) client. - # - # @see Transport::Base - # - class Curb - include Base - - # Performs the request by invoking {Transport::Base#perform_request} with a block. - # - # @return [Response] - # @see Transport::Base#perform_request - # - def perform_request(method, path, params={}, body=nil, headers=nil, opts={}) - super do |connection, url| - connection.connection.url = connection.full_url(path, params) - - case method - when 'HEAD' - connection.connection.set :nobody, true - when 'GET', 'POST', 'PUT', 'DELETE' - connection.connection.set :nobody, false - - connection.connection.put_data = __convert_to_json(body) if body - - if headers - if connection.connection.headers - connection.connection.headers.merge!(headers) - else - connection.connection.headers = headers - end - end - - else raise ArgumentError, "Unsupported HTTP method: #{method}" - end - - connection.connection.http(method.to_sym) - - response_headers = {} - response_headers['content-type'] = 'application/json' if connection.connection.header_str =~ /\/json/ - - Response.new connection.connection.response_code, - decompress_response(connection.connection.body_str), - response_headers - end - end - - # Builds and returns a connection - # - # @return [Connections::Connection] - # - def __build_connection(host, options={}, block=nil) - client = ::Curl::Easy.new - - apply_headers(client, options) - client.url = __full_url(host) - - if host[:user] - client.http_auth_types = host[:auth_type] || :basic - client.username = host[:user] - client.password = host[:password] - end - - client.instance_eval(&block) if block - - Connections::Connection.new :host => host, :connection => client - end - - # Returns an array of implementation specific connection errors. - # - # @return [Array] - # - def host_unreachable_exceptions - [ - ::Curl::Err::HostResolutionError, - ::Curl::Err::ConnectionFailedError, - ::Curl::Err::GotNothingError, - ::Curl::Err::RecvError, - ::Curl::Err::SendError, - ::Curl::Err::TimeoutError - ] - end - - private - - def user_agent_header(client) - @user_agent ||= begin - meta = ["RUBY_VERSION: #{RUBY_VERSION}"] - if RbConfig::CONFIG && RbConfig::CONFIG['host_os'] - meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}" - end - meta << "Curb #{Curl::CURB_VERSION}" - "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})" - end - end - end - end - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/http/faraday.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/http/faraday.rb deleted file mode 100644 index 0bacf9e8e8..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/http/faraday.rb +++ /dev/null @@ -1,96 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Transport - module Transport - module HTTP - - # The default transport implementation, using the [_Faraday_](https://rubygems.org/gems/faraday) - # library for abstracting the HTTP client. - # - # @see Transport::Base - # - class Faraday - include Base - - # Performs the request by invoking {Transport::Base#perform_request} with a block. - # - # @return [Response] - # @see Transport::Base#perform_request - # - def perform_request(method, path, params = {}, body = nil, headers = nil, opts = {}) - super do |connection, url| - headers = if connection.connection.headers - if !headers.nil? - connection.connection.headers.merge(headers) - else - connection.connection.headers - end - else - headers - end - - response = connection.connection.run_request(method.downcase.to_sym, - url, - ( body ? __convert_to_json(body) : nil ), - headers) - - Response.new response.status, decompress_response(response.body), response.headers - end - end - - # Builds and returns a connection - # - # @return [Connections::Connection] - # - def __build_connection(host, options={}, block=nil) - client = ::Faraday.new(__full_url(host), options, &block) - apply_headers(client, options) - Connections::Connection.new :host => host, :connection => client - end - - # Returns an array of implementation specific connection errors. - # - # @return [Array] - # - def host_unreachable_exceptions - [ - ::Faraday::ConnectionFailed, - ::Faraday::TimeoutError, - ::Faraday.const_defined?(:ServerError) ? ::Faraday::ServerError : nil, - ::Faraday::SSLError - ].compact - end - - private - - def user_agent_header(client) - @user_agent ||= begin - meta = ["RUBY_VERSION: #{RUBY_VERSION}"] - if RbConfig::CONFIG && RbConfig::CONFIG['host_os'] - meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}" - end - meta << "#{client.headers[USER_AGENT_STR]}" - "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})" - end - end - end - end - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/http/manticore.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/http/manticore.rb deleted file mode 100644 index 04ce369937..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/http/manticore.rb +++ /dev/null @@ -1,179 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'manticore' - -module Elasticsearch - module Transport - module Transport - module HTTP - # Alternative HTTP transport implementation for JRuby, - # using the [_Manticore_](https://github.com/cheald/manticore) client, - # - # @example HTTP - # - # require 'elasticsearch/transport/transport/http/manticore' - # - # client = Elasticsearch::Client.new transport_class: Elasticsearch::Transport::Transport::HTTP::Manticore - # - # client.transport.connections.first.connection - # => # - # - # client.info['status'] - # => 200 - # - # @example HTTPS (All SSL settings are optional, - # see http://www.rubydoc.info/gems/manticore/Manticore/Client:initialize) - # - # require 'elasticsearch/transport/transport/http/manticore' - # - # client = Elasticsearch::Client.new \ - # url: 'https://elasticsearch.example.com', - # transport_class: Elasticsearch::Transport::Transport::HTTP::Manticore, - # ssl: { - # truststore: '/tmp/truststore.jks', - # truststore_password: 'password', - # keystore: '/tmp/keystore.jks', - # keystore_password: 'secret', - # } - # - # client.transport.connections.first.connection - # => # - # - # client.info['status'] - # => 200 - # - # @see Transport::Base - # - class Manticore - include Base - - def initialize(arguments={}, &block) - @request_options = { headers: (arguments.dig(:transport_options, :headers) || {}) } - @manticore = build_client(arguments[:options] || {}) - super(arguments, &block) - end - - # Should just be run once at startup - def build_client(options={}) - client_options = options[:transport_options] || {} - client_options[:ssl] = options[:ssl] || {} - - @manticore = ::Manticore::Client.new(client_options) - end - - # Performs the request by invoking {Transport::Base#perform_request} with a block. - # - # @return [Response] - # @see Transport::Base#perform_request - # - def perform_request(method, path, params={}, body=nil, headers=nil, opts={}) - super do |connection, url| - params[:body] = __convert_to_json(body) if body - params[:headers] = headers if headers - params = params.merge @request_options - case method - when "GET" - resp = connection.connection.get(url, params) - when "HEAD" - resp = connection.connection.head(url, params) - when "PUT" - resp = connection.connection.put(url, params) - when "POST" - resp = connection.connection.post(url, params) - when "DELETE" - resp = connection.connection.delete(url, params) - else - raise ArgumentError.new "Method #{method} not supported" - end - Response.new resp.code, resp.read_body, resp.headers - end - end - - # Builds and returns a collection of connections. - # Each connection is a Manticore::Client - # - # @return [Connections::Collection] - # - def __build_connections - apply_headers(@request_options, options[:transport_options]) - apply_headers(@request_options, options) - - Connections::Collection.new \ - :connections => hosts.map { |host| - host[:protocol] = host[:scheme] || DEFAULT_PROTOCOL - host[:port] ||= DEFAULT_PORT - - host.delete(:user) # auth is not supported here. - host.delete(:password) # use the headers - - Connections::Connection.new \ - :host => host, - :connection => @manticore - }, - :selector_class => options[:selector_class], - :selector => options[:selector] - end - - # Closes all connections by marking them as dead - # and closing the underlying HttpClient instances - # - # @return [Connections::Collection] - # - def __close_connections - # The Manticore adapter uses a single long-lived instance - # of Manticore::Client, so we don't close the connections. - end - - # Returns an array of implementation specific connection errors. - # - # @return [Array] - # - def host_unreachable_exceptions - [ - ::Manticore::Timeout, - ::Manticore::SocketException, - ::Manticore::ClientProtocolException, - ::Manticore::ResolutionFailure - ] - end - - private - - def apply_headers(request_options, options) - headers = options&.[](:headers) || {} - headers[CONTENT_TYPE_STR] = find_value(headers, CONTENT_TYPE_REGEX) || DEFAULT_CONTENT_TYPE - headers[USER_AGENT_STR] = find_value(headers, USER_AGENT_REGEX) || user_agent_header - headers[ACCEPT_ENCODING] = GZIP if use_compression? - request_options[:headers].merge!(headers) - end - - def user_agent_header - @user_agent ||= begin - meta = ["RUBY_VERSION: #{JRUBY_VERSION}"] - if RbConfig::CONFIG && RbConfig::CONFIG['host_os'] - meta << "#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase} #{RbConfig::CONFIG['target_cpu']}" - end - meta << "Manticore #{::Manticore::VERSION}" - "elasticsearch-ruby/#{VERSION} (#{meta.join('; ')})" - end - end - end - end - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/loggable.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/loggable.rb deleted file mode 100644 index 03a304acad..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/loggable.rb +++ /dev/null @@ -1,85 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - - # Module to encapsulate all logging functionality. - # - # @since 7.0.0 - module Loggable - - # Log a debug message. - # - # @example Log a debug message. - # log_debug('Message') - # - # @param [ String ] message The message to log. - # - # @since 7.0.0 - def log_debug(message) - logger.debug(message) if logger && logger.debug? - end - - # Log an error message. - # - # @example Log an error message. - # log_error('Message') - # - # @param [ String ] message The message to log. - # - # @since 7.0.0 - def log_error(message) - logger.error(message) if logger && logger.error? - end - - # Log a fatal message. - # - # @example Log a fatal message. - # log_fatal('Message') - # - # @param [ String ] message The message to log. - # - # @since 7.0.0 - def log_fatal(message) - logger.fatal(message) if logger && logger.fatal? - end - - # Log an info message. - # - # @example Log an info message. - # log_info('Message') - # - # @param [ String ] message The message to log. - # - # @since 7.0.0 - def log_info(message) - logger.info(message) if logger && logger.info? - end - - # Log a warn message. - # - # @example Log a warn message. - # log_warn('Message') - # - # @param [ String ] message The message to log. - # - # @since 7.0.0 - def log_warn(message) - logger.warn(message) if logger && logger.warn? - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/response.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/response.rb deleted file mode 100644 index f1c7b84f80..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/response.rb +++ /dev/null @@ -1,37 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Transport - module Transport - # Wraps the response from Elasticsearch. - # - class Response - attr_reader :status, :body, :headers - - # @param status [Integer] Response status code - # @param body [String] Response body - # @param headers [Hash] Response headers - def initialize(status, body, headers={}) - @status, @body, @headers = status, body, headers - @body = body.force_encoding('UTF-8') if body.respond_to?(:force_encoding) - end - end - - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/serializer/multi_json.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/serializer/multi_json.rb deleted file mode 100644 index ab928f1c54..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/serializer/multi_json.rb +++ /dev/null @@ -1,53 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Transport - module Transport - module Serializer - - # An abstract class for implementing serializer implementations - # - module Base - # @param transport [Object] The instance of transport which uses this serializer - # - def initialize(transport=nil) - @transport = transport - end - end - - # A default JSON serializer (using [MultiJSON](http://rubygems.org/gems/multi_json)) - # - class MultiJson - include Base - - # De-serialize a Hash from JSON string - # - def load(string, options={}) - ::MultiJson.load(string, options) - end - - # Serialize a Hash to JSON string - # - def dump(object, options={}) - ::MultiJson.dump(object, options) - end - end - end - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/transport/sniffer.rb b/elasticsearch-transport/lib/elasticsearch/transport/transport/sniffer.rb deleted file mode 100644 index 7b320ed34e..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/transport/sniffer.rb +++ /dev/null @@ -1,102 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Transport - module Transport - - # Handles node discovery ("sniffing") - # - class Sniffer - PROTOCOL = 'http' - - attr_reader :transport - attr_accessor :timeout - - # @param transport [Object] A transport instance - # - def initialize(transport) - @transport = transport - @timeout = transport.options[:sniffer_timeout] || 1 - end - - # Retrieves the node list from the Elasticsearch's - # [_Nodes Info API_](https://www.elastic.co/guide/reference/api/admin-cluster-nodes-info/) - # and returns a normalized Array of information suitable for passing to transport. - # - # Shuffles the collection before returning it when the `randomize_hosts` option is set for transport. - # - # @return [Array] - # @raise [SnifferTimeoutError] - # - def hosts - Timeout::timeout(timeout, SnifferTimeoutError) do - nodes = perform_sniff_request.body - - hosts = nodes['nodes'].map do |id, info| - next unless info[PROTOCOL] - host, port = parse_publish_address(info[PROTOCOL]['publish_address']) - - { - id: id, - name: info['name'], - version: info['version'], - host: host, - port: port, - roles: info['roles'], - attributes: info['attributes'] - } - end.compact - - hosts.shuffle! if transport.options[:randomize_hosts] - hosts - end - end - - private - - def perform_sniff_request - transport.perform_request( - 'GET', '_nodes/http', {}, nil, nil, - reload_on_failure: false - ) - end - - def parse_publish_address(publish_address) - # publish_address is in the format hostname/ip:port - if publish_address =~ /\// - parts = publish_address.partition('/') - [ parts[0], parse_address_port(parts[2])[1] ] - else - parse_address_port(publish_address) - end - end - - def parse_address_port(publish_address) - # address is ipv6 - if publish_address =~ /[\[\]]/ - if parts = publish_address.match(/\A\[(.+)\](?::(\d+))?\z/) - [ parts[1], parts[2] ] - end - else - publish_address.split(':') - end - end - end - end - end -end diff --git a/elasticsearch-transport/lib/elasticsearch/transport/version.rb b/elasticsearch-transport/lib/elasticsearch/transport/version.rb deleted file mode 100644 index fee7f833dc..0000000000 --- a/elasticsearch-transport/lib/elasticsearch/transport/version.rb +++ /dev/null @@ -1,22 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Transport - VERSION = '8.0.0'.freeze - end -end diff --git a/elasticsearch-transport/spec/elasticsearch/connections/collection_spec.rb b/elasticsearch-transport/spec/elasticsearch/connections/collection_spec.rb deleted file mode 100644 index a900f26e61..0000000000 --- a/elasticsearch-transport/spec/elasticsearch/connections/collection_spec.rb +++ /dev/null @@ -1,266 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'spec_helper' - -describe Elasticsearch::Transport::Transport::Connections::Collection do - - describe '#initialize' do - - let(:collection) do - described_class.new - end - - it 'has an empty list of connections as a default' do - expect(collection.connections).to be_empty - end - - it 'has a default selector class' do - expect(collection.selector).not_to be_nil - end - - context 'when a selector class is specified' do - - let(:collection) do - described_class.new(selector_class: Elasticsearch::Transport::Transport::Connections::Selector::Random) - end - - it 'sets the selector' do - expect(collection.selector).to be_a(Elasticsearch::Transport::Transport::Connections::Selector::Random) - end - end - end - - describe '#get_connection' do - - let(:collection) do - described_class.new(selector_class: Elasticsearch::Transport::Transport::Connections::Selector::Random) - end - - before do - expect(collection.selector).to receive(:select).and_return('OK') - end - - it 'uses the selector to select a connection' do - expect(collection.get_connection).to eq('OK') - end - end - - describe '#hosts' do - - let(:collection) do - described_class.new(connections: [ double('connection', host: 'A'), - double('connection', host: 'B') ]) - end - - it 'returns a list of hosts' do - expect(collection.hosts).to eq([ 'A', 'B']) - end - end - - describe 'enumerable' do - - let(:collection) do - described_class.new(connections: [ double('connection', host: 'A', dead?: false), - double('connection', host: 'B', dead?: false) ]) - end - - describe '#map' do - - it 'responds to the method' do - expect(collection.map { |c| c.host.downcase }).to eq(['a', 'b']) - end - end - - describe '#[]' do - - it 'responds to the method' do - expect(collection[0].host).to eq('A') - expect(collection[1].host).to eq('B') - end - end - - describe '#size' do - - it 'responds to the method' do - expect(collection.size).to eq(2) - end - end - - context 'when a connection is marked as dead' do - - let(:collection) do - described_class.new(connections: [ double('connection', host: 'A', dead?: true), - double('connection', host: 'B', dead?: false) ]) - end - - it 'does not enumerate the dead connections' do - expect(collection.size).to eq(1) - expect(collection.collect { |c| c.host }).to eq(['B']) - end - - context '#alive' do - - it 'enumerates the alive connections' do - expect(collection.alive.collect { |c| c.host }).to eq(['B']) - end - end - - context '#dead' do - - it 'enumerates the alive connections' do - expect(collection.dead.collect { |c| c.host }).to eq(['A']) - end - end - end - end - - describe '#add' do - - let(:collection) do - described_class.new(connections: [ double('connection', host: 'A', dead?: false), - double('connection', host: 'B', dead?: false) ]) - end - - context 'when an array is provided' do - - before do - collection.add([double('connection', host: 'C', dead?: false), - double('connection', host: 'D', dead?: false)]) - end - - it 'adds the connections' do - expect(collection.size).to eq(4) - end - end - - context 'when an element is provided' do - - before do - collection.add(double('connection', host: 'C', dead?: false)) - end - - it 'adds the connection' do - expect(collection.size).to eq(3) - end - end - end - - describe '#remove' do - - let(:connections) do - [ double('connection', host: 'A', dead?: false), - double('connection', host: 'B', dead?: false) ] - end - - let(:collection) do - described_class.new(connections: connections) - end - - context 'when an array is provided' do - - before do - collection.remove(connections) - end - - it 'removes the connections' do - expect(collection.size).to eq(0) - end - end - - context 'when an element is provided' do - - let(:connections) do - [ double('connection', host: 'A', dead?: false), - double('connection', host: 'B', dead?: false) ] - end - - before do - collection.remove(connections.first) - end - - it 'removes the connection' do - expect(collection.size).to eq(1) - end - end - end - - describe '#get_connection' do - - context 'when all connections are dead' do - - let(:connection_a) do - Elasticsearch::Transport::Transport::Connections::Connection.new(host: { host: 'A' }) - end - - let(:connection_b) do - Elasticsearch::Transport::Transport::Connections::Connection.new(host: { host: 'B' }) - end - - let(:collection) do - described_class.new(connections: [connection_a, connection_b]) - end - - before do - connection_a.dead!.dead! - connection_b.dead! - end - - it 'returns the connection with the least failures' do - expect(collection.get_connection.host[:host]).to eq('B') - end - end - - context 'when multiple threads are used' do - - let(:connections) do - 20.times.collect do |i| - Elasticsearch::Transport::Transport::Connections::Connection.new(host: { host: i }) - end - end - - let(:collection) do - described_class.new(connections: connections) - end - - it 'allows threads to select connections in parallel' do - expect(10.times.collect do - threads = [] - 20.times do - threads << Thread.new do - collection.get_connection - end - end - threads.map { |t| t.join } - collection.get_connection.host[:host] - end).to eq((0..9).to_a) - end - - it 'always returns a connection' do - threads = 20.times.map do - Thread.new do - 20.times.map do - collection.get_connection.dead! - end - end - end - - expect(threads.flat_map(&:value).size).to eq(400) - end - end - end -end diff --git a/elasticsearch-transport/spec/elasticsearch/connections/selector_spec.rb b/elasticsearch-transport/spec/elasticsearch/connections/selector_spec.rb deleted file mode 100644 index 3da4c920e2..0000000000 --- a/elasticsearch-transport/spec/elasticsearch/connections/selector_spec.rb +++ /dev/null @@ -1,174 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'spec_helper' - -describe Elasticsearch::Transport::Transport::Connections::Selector do - - before do - class BackupStrategySelector - include Elasticsearch::Transport::Transport::Connections::Selector::Base - - def select(options={}) - connections.reject do |c| - c.host[:attributes] && c.host[:attributes][:backup] - end.sample - end - end - end - - after do - Object.send(:remove_const, :BackupStrategySelector) - end - - let(:backup_strategy_selector) do - BackupStrategySelector.new - end - - describe 'the Random selector' do - - let(:connections) do - [1, 2] - end - - let(:selector) do - described_class::Random.new(connections: connections) - end - - it 'is initialized with connections' do - expect(selector.connections).to eq(connections) - end - - describe '#select' do - - let(:connections) do - (0..19).to_a - end - - it 'returns a connection' do - expect(selector.select).to be_a(Integer) - end - - context 'when multiple threads are used' do - - it 'allows threads to select connections in parallel' do - expect(10.times.collect do - threads = [] - 20.times do - threads << Thread.new do - selector.select - end - end - threads.map { |t| t.join } - selector.select - end).to all(be_a(Integer)) - end - end - end - end - - describe 'the RoundRobin selector' do - - let(:connections) do - ['A', 'B', 'C'] - end - - let(:selector) do - described_class::RoundRobin.new(connections: connections) - end - - it 'is initialized with connections' do - expect(selector.connections).to eq(connections) - end - - describe '#select' do - - it 'rotates over the connections' do - expect(selector.select).to eq('A') - expect(selector.select).to eq('B') - expect(selector.select).to eq('C') - expect(selector.select).to eq('A') - end - - context 'when multiple threads are used' do - - let(:connections) do - (0..19).to_a - end - - it 'returns a connection' do - expect(selector.select).to be_a(Integer) - end - - it 'allows threads to select connections in parallel' do - expect(10.times.collect do - threads = [] - 20.times do - threads << Thread.new do - selector.select - end - end - threads.map { |t| t.join } - selector.select - end).to eq((0..9).to_a) - end - end - end - end - - describe 'a custom selector' do - - let(:connections) do - [ double(host: { hostname: 'host1' }), - double(host: { hostname: 'host2', attributes: { backup: true } }) ] - end - - let(:selector) do - BackupStrategySelector.new(connections: connections) - end - - it 'is initialized with connections' do - expect(selector.connections).to eq(connections) - end - - describe '#select' do - - it 'applies the custom strategy' do - 10.times { expect(selector.select.host[:hostname]).to eq('host1') } - end - end - end - - context 'when the Base module is included in a class' do - - before do - class ExampleSelector - include Elasticsearch::Transport::Transport::Connections::Selector::Base - end - end - - after do - Object.send(:remove_const, :ExampleSelector) - end - - it 'requires the #select method to be redefined' do - expect { - ExampleSelector.new.select - }.to raise_exception(NoMethodError) - end - end -end diff --git a/elasticsearch-transport/spec/elasticsearch/transport/base_spec.rb b/elasticsearch-transport/spec/elasticsearch/transport/base_spec.rb deleted file mode 100644 index c93c25061f..0000000000 --- a/elasticsearch-transport/spec/elasticsearch/transport/base_spec.rb +++ /dev/null @@ -1,264 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'spec_helper' - -describe Elasticsearch::Transport::Transport::Base do - context 'when a host is printed in a logged message' do - shared_examples_for 'a redacted string' do - let(:client) do - Elasticsearch::Transport::Client.new(arguments) - end - - let(:logger) do - double('logger', error?: true, error: '') - end - - it 'does not include the password in the logged string' do - expect(logger).not_to receive(:error).with(/secret_password/) - expect { - client.perform_request('GET', '/_cluster/stats') - }.to raise_exception(Faraday::ConnectionFailed) - end - - it 'replaces the password with the string \'REDACTED\'' do - expect(logger).to receive(:error).with(/REDACTED/) - expect { - client.perform_request('GET', '/_cluster/stats') - }.to raise_exception(Faraday::ConnectionFailed) - end - end - - context 'when the user and password are provided as separate arguments' do - let(:arguments) do - { - hosts: 'fake', - logger: logger, - password: 'secret_password', - user: 'test' - } - end - - it_behaves_like 'a redacted string' - end - - context 'when the user and password are provided in the string URI' do - let(:arguments) do - { - hosts: 'http://test:secret_password@fake', - logger: logger - } - end - - it_behaves_like 'a redacted string' - end - - context 'when the user and password are provided in the URI object' do - let(:arguments) do - { - hosts: URI.parse('http://test:secret_password@fake'), - logger: logger - } - end - - it_behaves_like 'a redacted string' - end - end - - context 'when reload_on_failure is true and and hosts are unreachable' do - let(:client) do - Elasticsearch::Transport::Client.new(arguments) - end - - let(:arguments) do - { - hosts: ['http://unavailable:9200', 'http://unavailable:9201'], - reload_on_failure: true, - sniffer_timeout: 5 - } - end - - it 'raises an exception' do - expect { client.perform_request('GET', '/info') }.to raise_exception(Faraday::ConnectionFailed) - end - end - - context 'when the client has `retry_on_failure` set to an integer' do - let(:client) do - Elasticsearch::Transport::Client.new(arguments) - end - - let(:arguments) do - { - hosts: ['http://unavailable:9200', 'http://unavailable:9201'], - retry_on_failure: 2 - } - end - - context 'when `perform_request` is called without a `retry_on_failure` option value' do - before do - expect(client.transport).to receive(:get_connection).exactly(3).times.and_call_original - end - - it 'uses the client `retry_on_failure` value' do - expect { - client.transport.perform_request('GET', '/info') - }.to raise_exception(Faraday::ConnectionFailed) - end - end - - context 'when `perform_request` is called with a `retry_on_status` option value' do - before do - expect(client.transport).to receive(:__raise_transport_error).exactly(6).times.and_call_original - end - - let(:arguments) do - { - hosts: ELASTICSEARCH_HOSTS, - retry_on_status: ['404'] - } - end - - it 'retries on 404 status the specified number of max_retries' do - expect do - client.transport.perform_request('GET', 'myindex/_doc/1?routing=FOOBARBAZ', {}, nil, nil, retry_on_failure: 5) - end.to raise_exception(Elasticsearch::Transport::Transport::Errors::NotFound) - end - end - - context 'when `perform_request` is called with a `retry_on_failure` option value' do - before do - expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original - end - - it 'uses the option `retry_on_failure` value' do - expect do - client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5) - end.to raise_exception(Faraday::ConnectionFailed) - end - end - end - - context 'when the client has `retry_on_failure` set to true' do - let(:client) do - Elasticsearch::Transport::Client.new(arguments) - end - - let(:arguments) do - { - hosts: ['http://unavailable:9200', 'http://unavailable:9201'], - retry_on_failure: true - } - end - - context 'when `perform_request` is called without a `retry_on_failure` option value' do - before do - expect(client.transport).to receive(:get_connection).exactly(4).times.and_call_original - end - - it 'uses the default `MAX_RETRIES` value' do - expect { - client.transport.perform_request('GET', '/info') - }.to raise_exception(Faraday::ConnectionFailed) - end - end - - context 'when `perform_request` is called with a `retry_on_failure` option value' do - before do - expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original - end - - it 'uses the option `retry_on_failure` value' do - expect { - client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5) - }.to raise_exception(Faraday::ConnectionFailed) - end - end - end - - context 'when the client has `retry_on_failure` set to false' do - let(:client) do - Elasticsearch::Transport::Client.new(arguments) - end - - let(:arguments) do - { - hosts: ['http://unavailable:9200', 'http://unavailable:9201'], - retry_on_failure: false - } - end - - context 'when `perform_request` is called without a `retry_on_failure` option value' do - before do - expect(client.transport).to receive(:get_connection).once.and_call_original - end - - it 'does not retry' do - expect { - client.transport.perform_request('GET', '/info') - }.to raise_exception(Faraday::ConnectionFailed) - end - end - - context 'when `perform_request` is called with a `retry_on_failure` option value' do - - before do - expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original - end - - it 'uses the option `retry_on_failure` value' do - expect { - client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5) - }.to raise_exception(Faraday::ConnectionFailed) - end - end - end - - context 'when the client has no `retry_on_failure` set' do - let(:client) do - Elasticsearch::Transport::Client.new(arguments) - end - - let(:arguments) do - { hosts: ['http://unavailable:9200', 'http://unavailable:9201'] } - end - - context 'when `perform_request` is called without a `retry_on_failure` option value' do - before do - expect(client.transport).to receive(:get_connection).exactly(1).times.and_call_original - end - - it 'does not retry' do - expect do - client.transport.perform_request('GET', '/info') - end.to raise_exception(Faraday::ConnectionFailed) - end - end - - context 'when `perform_request` is called with a `retry_on_failure` option value' do - before do - expect(client.transport).to receive(:get_connection).exactly(6).times.and_call_original - end - - it 'uses the option `retry_on_failure` value' do - expect do - client.transport.perform_request('GET', '/info', {}, nil, nil, retry_on_failure: 5) - end.to raise_exception(Faraday::ConnectionFailed) - end - end - end -end diff --git a/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb b/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb deleted file mode 100644 index 97ace95ab3..0000000000 --- a/elasticsearch-transport/spec/elasticsearch/transport/client_spec.rb +++ /dev/null @@ -1,1661 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'spec_helper' - -describe Elasticsearch::Transport::Client do - let(:client) do - described_class.new.tap do |_client| - allow(_client).to receive(:__build_connections) - end - end - - it 'has a default transport' do - expect(client.transport).to be_a(Elasticsearch::Transport::Client::DEFAULT_TRANSPORT_CLASS) - end - - it 'preserves the Faraday default user agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/Faraday/) - end - - it 'identifies the Ruby client in the User-Agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/elasticsearch-ruby\/#{Elasticsearch::Transport::VERSION}/) - end - - it 'identifies the Ruby version in the User-Agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/#{RUBY_VERSION}/) - end - - it 'identifies the host_os in the User-Agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase}/) - end - - it 'identifies the target_cpu in the User-Agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/#{RbConfig::CONFIG['target_cpu']}/) - end - - it 'sets the \'Content-Type\' header to \'application/json\' by default' do - expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('application/json') - end - - it 'uses localhost by default' do - expect(client.transport.hosts[0][:host]).to eq('localhost') - end - - context 'when a User-Agent header is specified as client option' do - let(:client) do - described_class.new(transport_options: { headers: { 'User-Agent' => 'testing' } }) - end - - it 'sets the specified User-Agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to eq('testing') - end - end - - - context 'when a user-agent header is specified as client option in lower-case' do - - let(:client) do - described_class.new(transport_options: { headers: { 'user-agent' => 'testing' } }) - end - - it 'sets the specified User-Agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to eq('testing') - end - end - - context 'when a Content-Type header is specified as client option' do - - let(:client) do - described_class.new(transport_options: { headers: { 'Content-Type' => 'testing' } }) - end - - it 'sets the specified Content-Type header' do - expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('testing') - end - end - - context 'when a content-type header is specified as client option in lower-case' do - - let(:client) do - described_class.new(transport_options: { headers: { 'content-type' => 'testing' } }) - end - - it 'sets the specified Content-Type header' do - expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('testing') - end - end - - context 'when the Curb transport class is used', unless: jruby? do - - let(:client) do - described_class.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Curb) - end - - it 'preserves the Curb default user agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/Curb/) - end - - it 'identifies the Ruby client in the User-Agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/elasticsearch-ruby\/#{Elasticsearch::Transport::VERSION}/) - end - - it 'identifies the Ruby version in the User-Agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/#{RUBY_VERSION}/) - end - - it 'identifies the host_os in the User-Agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/#{RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase}/) - end - - it 'identifies the target_cpu in the User-Agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to match(/#{RbConfig::CONFIG['target_cpu']}/) - end - - it 'sets the \'Content-Type\' header to \'application/json\' by default' do - expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('application/json') - end - - it 'uses localhost by default' do - expect(client.transport.hosts[0][:host]).to eq('localhost') - end - - context 'when a User-Agent header is specified as a client option' do - - let(:client) do - described_class.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Curb, - transport_options: { headers: { 'User-Agent' => 'testing' } }) - end - - it 'sets the specified User-Agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to eq('testing') - end - end - - context 'when a user-agent header is specified as a client option as lower-case' do - - let(:client) do - described_class.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Curb, - transport_options: { headers: { 'user-agent' => 'testing' } }) - end - - it 'sets the specified User-Agent header' do - expect(client.transport.connections.first.connection.headers['User-Agent']).to eq('testing') - end - end - - context 'when a Content-Type header is specified as client option' do - - let(:client) do - described_class.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Curb, - transport_options: { headers: { 'Content-Type' => 'testing' } }) - end - - it 'sets the specified Content-Type header' do - expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('testing') - end - end - - context 'when a content-type header is specified as client option in lower-case' do - - let(:client) do - described_class.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Curb, - transport_options: { headers: { 'content-type' => 'testing' } }) - end - - it 'sets the specified Content-Type header' do - expect(client.transport.connections.first.connection.headers['Content-Type']).to eq('testing') - end - end - end - - describe 'adapter' do - context 'when no adapter is specified' do - fork do - let(:client) { described_class.new } - let(:adapter) { client.transport.connections.all.first.connection.builder.adapter } - - it 'uses Faraday NetHttp' do - expect(adapter).to eq Faraday::Adapter::NetHttp - end - end unless jruby? - end - - context 'when the adapter is patron' do - let(:adapter) do - client.transport.connections.all.first.connection.builder.adapter - end - - let(:client) do - described_class.new(adapter: :patron, enable_meta_header: false) - end - - it 'uses Faraday with the adapter' do - expect(adapter).to eq Faraday::Adapter::Patron - end - end - - context 'when the adapter is typhoeus' do - let(:adapter) do - client.transport.connections.all.first.connection.builder.adapter - end - - let(:client) do - described_class.new(adapter: :typhoeus, enable_meta_header: false) - end - - it 'uses Faraday with the adapter' do - expect(adapter).to eq Faraday::Adapter::Typhoeus - end - end unless jruby? - - context 'when the adapter is specified as a string key' do - let(:adapter) do - client.transport.connections.all.first.connection.builder.adapter - end - - let(:client) do - described_class.new(adapter: :patron, enable_meta_header: false) - end - - it 'uses Faraday with the adapter' do - expect(adapter).to eq Faraday::Adapter::Patron - end - end - - context 'when the adapter can be detected', unless: jruby? do - around do |example| - require 'patron'; load 'patron.rb' - example.run - end - - let(:adapter) do - client.transport.connections.all.first.connection.builder.adapter - end - - it 'uses the detected adapter' do - expect(adapter).to eq Faraday::Adapter::Patron - end - end - - context 'when the Faraday adapter is configured' do - let(:client) do - described_class.new do |faraday| - faraday.adapter :patron - faraday.response :logger - end - end - - let(:adapter) do - client.transport.connections.all.first.connection.builder.adapter - end - - let(:handlers) do - client.transport.connections.all.first.connection.builder.handlers - end - - it 'sets the adapter' do - expect(adapter).to eq Faraday::Adapter::Patron - end - - it 'sets the logger' do - expect(handlers).to include(Faraday::Response::Logger) - end - end - end - - shared_examples_for 'a client that extracts hosts' do - context 'when the host is a String' do - context 'when there is a protocol specified' do - context 'when credentials are specified \'http://USERNAME:PASSWORD@myhost:8080\'' do - let(:host) do - 'http://USERNAME:PASSWORD@myhost:8080' - end - - it 'extracts the credentials' do - expect(hosts[0][:user]).to eq('USERNAME') - expect(hosts[0][:password]).to eq('PASSWORD') - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the port' do - expect(hosts[0][:port]).to be(8080) - end - end - - context 'when there is a trailing slash \'http://myhost/\'' do - let(:host) do - 'http://myhost/' - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - expect(hosts[0][:scheme]).to eq('http') - expect(hosts[0][:path]).to eq('') - end - - it 'extracts the scheme' do - expect(hosts[0][:scheme]).to eq('http') - end - - it 'extracts the path' do - expect(hosts[0][:path]).to eq('') - end - end - - context 'when there is a trailing slash with a path \'http://myhost/foo/bar/\'' do - let(:host) do - 'http://myhost/foo/bar/' - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - expect(hosts[0][:scheme]).to eq('http') - expect(hosts[0][:path]).to eq('/foo/bar') - end - end - - context 'when the protocol is http' do - context 'when there is no port specified \'http://myhost\'' do - let(:host) do - 'http://myhost' - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:protocol]).to eq('http') - end - - it 'defaults to port 9200' do - expect(hosts[0][:port]).to be(9200) - end - end - - context 'when there is a port specified \'http://myhost:7101\'' do - let(:host) do - 'http://myhost:7101' - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:protocol]).to eq('http') - end - - it 'extracts the port' do - expect(hosts[0][:port]).to be(7101) - end - - context 'when there is a path specified \'http://myhost:7101/api\'' do - let(:host) do - 'http://myhost:7101/api' - end - - it 'sets the path' do - expect(hosts[0][:host]).to eq('myhost') - expect(hosts[0][:protocol]).to eq('http') - expect(hosts[0][:path]).to eq('/api') - expect(hosts[0][:port]).to be(7101) - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:protocol]).to eq('http') - end - - it 'extracts the port' do - expect(hosts[0][:port]).to be(7101) - end - - it 'extracts the path' do - expect(hosts[0][:path]).to eq('/api') - end - end - end - end - - context 'when the protocol is https' do - context 'when there is no port specified \'https://myhost\'' do - let(:host) do - 'https://myhost' - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:protocol]).to eq('https') - end - - it 'defaults to port 443' do - expect(hosts[0][:port]).to be(443) - end - end - - context 'when there is a port specified \'https://myhost:7101\'' do - let(:host) do - 'https://myhost:7101' - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:protocol]).to eq('https') - end - - it 'extracts the port' do - expect(hosts[0][:port]).to be(7101) - end - - context 'when there is a path specified \'https://myhost:7101/api\'' do - - let(:host) do - 'https://myhost:7101/api' - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:protocol]).to eq('https') - end - - it 'extracts the port' do - expect(hosts[0][:port]).to be(7101) - end - - it 'extracts the path' do - expect(hosts[0][:path]).to eq('/api') - end - end - end - - context 'when IPv6 format is used' do - - around do |example| - original_setting = Faraday.ignore_env_proxy - Faraday.ignore_env_proxy = true - example.run - Faraday.ignore_env_proxy = original_setting - end - - let(:host) do - 'https://[2090:db8:85a3:9811::1f]:8080' - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('[2090:db8:85a3:9811::1f]') - end - - it 'extracts the protocol' do - expect(hosts[0][:protocol]).to eq('https') - end - - it 'extracts the port' do - expect(hosts[0][:port]).to be(8080) - end - - it 'creates the correct full url' do - expect(client.transport.__full_url(client.transport.hosts[0])).to eq('https://[2090:db8:85a3:9811::1f]:8080') - end - end - end - end - - context 'when no protocol is specified \'myhost\'' do - - let(:host) do - 'myhost' - end - - it 'defaults to http' do - expect(hosts[0][:host]).to eq('myhost') - expect(hosts[0][:protocol]).to eq('http') - end - - it 'uses port 9200' do - expect(hosts[0][:port]).to be(9200) - end - end - end - - context 'when the host is a Hash' do - - let(:host) do - { :host => 'myhost', :scheme => 'https' } - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:protocol]).to eq('https') - end - - it 'extracts the port' do - expect(hosts[0][:port]).to be(9200) - end - - context 'when IPv6 format is used' do - - around do |example| - original_setting = Faraday.ignore_env_proxy - Faraday.ignore_env_proxy = true - example.run - Faraday.ignore_env_proxy = original_setting - end - - let(:host) do - { host: '[2090:db8:85a3:9811::1f]', scheme: 'https', port: '443' } - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('[2090:db8:85a3:9811::1f]') - expect(hosts[0][:scheme]).to eq('https') - expect(hosts[0][:port]).to be(443) - end - - it 'creates the correct full url' do - expect(client.transport.__full_url(client.transport.hosts[0])).to eq('https://[2090:db8:85a3:9811::1f]:443') - end - end - - context 'when the host is localhost as a IPv6 address' do - - around do |example| - original_setting = Faraday.ignore_env_proxy - Faraday.ignore_env_proxy = true - example.run - Faraday.ignore_env_proxy = original_setting - end - - let(:host) do - { host: '[::1]' } - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('[::1]') - expect(hosts[0][:port]).to be(9200) - end - - it 'creates the correct full url' do - expect(client.transport.__full_url(client.transport.hosts[0])).to eq('http://[::1]:9200') - end - end - - context 'when the port is specified as a String' do - - let(:host) do - { host: 'myhost', scheme: 'https', port: '443' } - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:scheme]).to eq('https') - end - - it 'converts the port to an integer' do - expect(hosts[0][:port]).to be(443) - end - end - - context 'when the port is specified as an Integer' do - - let(:host) do - { host: 'myhost', scheme: 'https', port: 443 } - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:scheme]).to eq('https') - end - - it 'extracts port as an integer' do - expect(hosts[0][:port]).to be(443) - end - end - end - - context 'when the hosts are a Hashie:Mash' do - - let(:host) do - Hashie::Mash.new(host: 'myhost', scheme: 'https') - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:scheme]).to eq('https') - end - - it 'converts the port to an integer' do - expect(hosts[0][:port]).to be(9200) - end - - context 'when the port is specified as a String' do - - let(:host) do - Hashie::Mash.new(host: 'myhost', scheme: 'https', port: '443') - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:scheme]).to eq('https') - end - - it 'converts the port to an integer' do - expect(hosts[0][:port]).to be(443) - end - end - - context 'when the port is specified as an Integer' do - - let(:host) do - Hashie::Mash.new(host: 'myhost', scheme: 'https', port: 443) - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:scheme]).to eq('https') - end - - it 'extracts port as an integer' do - expect(hosts[0][:port]).to be(443) - end - end - end - - context 'when the hosts are an array' do - - context 'when there is one host' do - - let(:host) do - ['myhost'] - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:protocol]).to eq('http') - end - - it 'defaults to port 9200' do - expect(hosts[0][:port]).to be(9200) - end - end - - context 'when there is one host with a protocol and no port' do - - let(:host) do - ['http://myhost'] - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:scheme]).to eq('http') - end - - it 'defaults to port 9200' do - expect(hosts[0][:port]).to be(9200) - end - end - - context 'when there is one host with a protocol and the default http port explicitly provided' do - let(:host) do - ['http://myhost:80'] - end - - it 'respects the explicit port' do - expect(hosts[0][:port]).to be(80) - end - end - - context 'when there is one host with a protocol and the default https port explicitly provided' do - let(:host) do - ['https://myhost:443'] - end - - it 'respects the explicit port' do - expect(hosts[0][:port]).to be(443) - end - end - - context 'when there is one host with a protocol and no port' do - - let(:host) do - ['https://myhost'] - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:scheme]).to eq('https') - end - - it 'defaults to port 443' do - expect(hosts[0][:port]).to be(443) - end - end - - context 'when there is one host with a protocol, path, and no port' do - - let(:host) do - ['http://myhost/foo/bar'] - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - end - - it 'extracts the protocol' do - expect(hosts[0][:scheme]).to eq('http') - end - - it 'defaults to port 9200' do - expect(hosts[0][:port]).to be(9200) - end - - it 'extracts the path' do - expect(hosts[0][:path]).to eq('/foo/bar') - end - end - - context 'when there is more than one host' do - - let(:host) do - ['host1', 'host2'] - end - - it 'extracts the hosts' do - expect(hosts[0][:host]).to eq('host1') - expect(hosts[0][:protocol]).to eq('http') - expect(hosts[0][:port]).to be(9200) - expect(hosts[1][:host]).to eq('host2') - expect(hosts[1][:protocol]).to eq('http') - expect(hosts[1][:port]).to be(9200) - end - end - - context 'when ports are also specified' do - - let(:host) do - ['host1:1000', 'host2:2000'] - end - - it 'extracts the hosts' do - expect(hosts[0][:host]).to eq('host1') - expect(hosts[0][:protocol]).to eq('http') - expect(hosts[0][:port]).to be(1000) - expect(hosts[1][:host]).to eq('host2') - expect(hosts[1][:protocol]).to eq('http') - expect(hosts[1][:port]).to be(2000) - end - end - end - - context 'when the hosts is an instance of URI' do - - let(:host) do - URI.parse('https://USERNAME:PASSWORD@myhost:4430') - end - - it 'extracts the host' do - expect(hosts[0][:host]).to eq('myhost') - expect(hosts[0][:scheme]).to eq('https') - expect(hosts[0][:port]).to be(4430) - expect(hosts[0][:user]).to eq('USERNAME') - expect(hosts[0][:password]).to eq('PASSWORD') - end - end - - context 'when the hosts is invalid' do - let(:host) do - 123 - end - - it 'extracts the host' do - expect { - hosts - }.to raise_exception(ArgumentError) - end - end - end - - context 'when hosts are specified with the \'host\' key' do - let(:client) do - described_class.new(host: ['host1', 'host2', 'host3', 'host4'], randomize_hosts: true) - end - - let(:hosts) do - client.transport.hosts - end - - it 'sets the hosts in random order' do - expect(hosts.all? { |host| client.transport.hosts.include?(host) }).to be(true) - end - end - - context 'when hosts are specified with the \'host\' key as a String' do - let(:client) do - described_class.new('host' => ['host1', 'host2', 'host3', 'host4'], 'randomize_hosts' => true) - end - - let(:hosts) do - client.transport.hosts - end - - it 'sets the hosts in random order' do - expect(hosts.all? { |host| client.transport.hosts.include?(host) }).to be(true) - end - end - - context 'when hosts are specified with the \'hosts\' key' do - let(:client) do - described_class.new(hosts: host) - end - - let(:hosts) do - client.transport.hosts - end - - it_behaves_like 'a client that extracts hosts' - end - - context 'when hosts are specified with the \'hosts\' key as a String' do - let(:client) do - described_class.new('hosts' => host) - end - - let(:hosts) do - client.transport.hosts - end - - it_behaves_like 'a client that extracts hosts' - end - - context 'when hosts are specified with the \'url\' key' do - let(:client) do - described_class.new(url: host) - end - - let(:hosts) do - client.transport.hosts - end - - it_behaves_like 'a client that extracts hosts' - end - - context 'when hosts are specified with the \'url\' key as a String' do - let(:client) do - described_class.new('url' => host) - end - - let(:hosts) do - client.transport.hosts - end - - it_behaves_like 'a client that extracts hosts' - end - - context 'when hosts are specified with the \'urls\' key' do - let(:client) do - described_class.new(urls: host) - end - - let(:hosts) do - client.transport.hosts - end - - it_behaves_like 'a client that extracts hosts' - end - - context 'when hosts are specified with the \'urls\' key as a String' do - let(:client) do - described_class.new('urls' => host) - end - - let(:hosts) do - client.transport.hosts - end - - it_behaves_like 'a client that extracts hosts' - end - - context 'when the URL is set in the ELASTICSEARCH_URL environment variable' do - - context 'when there is only one host specified' do - - around do |example| - before_url = ENV['ELASTICSEARCH_URL'] - ENV['ELASTICSEARCH_URL'] = 'example.com' - example.run - ENV['ELASTICSEARCH_URL'] = before_url - end - - it 'sets the host' do - expect(client.transport.hosts[0][:host]).to eq('example.com') - expect(client.transport.hosts.size).to eq(1) - end - end - - context 'when mutliple hosts are specified as a comma-separated String list' do - - around do |example| - before_url = ENV['ELASTICSEARCH_URL'] - ENV['ELASTICSEARCH_URL'] = 'example.com, other.com' - example.run - ENV['ELASTICSEARCH_URL'] = before_url - end - - it 'sets the hosts' do - expect(client.transport.hosts[0][:host]).to eq('example.com') - expect(client.transport.hosts[1][:host]).to eq('other.com') - expect(client.transport.hosts.size).to eq(2) - end - end - end - - context 'when options are defined' do - - context 'when scheme is specified' do - - let(:client) do - described_class.new(scheme: 'https') - end - - it 'sets the scheme' do - expect(client.transport.connections[0].full_url('')).to match(/https/) - end - end - - context 'when scheme is specified as a String key' do - - let(:client) do - described_class.new('scheme' => 'https') - end - - it 'sets the scheme' do - expect(client.transport.connections[0].full_url('')).to match(/https/) - end - end - - context 'when user and password are specified' do - - let(:client) do - described_class.new(user: 'USERNAME', password: 'PASSWORD') - end - - it 'sets the user and password' do - expect(client.transport.connections[0].full_url('')).to match(/USERNAME/) - expect(client.transport.connections[0].full_url('')).to match(/PASSWORD/) - end - - context 'when the connections are reloaded' do - - before do - allow(client.transport.sniffer).to receive(:hosts).and_return([{ host: 'foobar', port: 4567, id: 'foobar4567' }]) - client.transport.reload_connections! - end - - it 'sets keeps user and password' do - expect(client.transport.connections[0].full_url('')).to match(/USERNAME/) - expect(client.transport.connections[0].full_url('')).to match(/PASSWORD/) - expect(client.transport.connections[0].full_url('')).to match(/foobar/) - end - end - end - - context 'when user and password are specified as String keys' do - - let(:client) do - described_class.new('user' => 'USERNAME', 'password' => 'PASSWORD') - end - - it 'sets the user and password' do - expect(client.transport.connections[0].full_url('')).to match(/USERNAME/) - expect(client.transport.connections[0].full_url('')).to match(/PASSWORD/) - end - - context 'when the connections are reloaded' do - - before do - allow(client.transport.sniffer).to receive(:hosts).and_return([{ host: 'foobar', port: 4567, id: 'foobar4567' }]) - client.transport.reload_connections! - end - - it 'sets keeps user and password' do - expect(client.transport.connections[0].full_url('')).to match(/USERNAME/) - expect(client.transport.connections[0].full_url('')).to match(/PASSWORD/) - expect(client.transport.connections[0].full_url('')).to match(/foobar/) - end - end - end - - context 'when port is specified' do - - let(:client) do - described_class.new(host: 'node1', port: 1234) - end - - it 'sets the port' do - expect(client.transport.connections[0].full_url('')).to match(/1234/) - end - end - - context 'when the log option is true' do - - let(:client) do - described_class.new(log: true) - end - - it 'has a default logger for transport' do - expect(client.transport.logger.info).to eq(described_class::DEFAULT_LOGGER.call.info) - end - end - - context 'when the trace option is true' do - - let(:client) do - described_class.new(trace: true) - end - - it 'has a default logger for transport' do - expect(client.transport.tracer.info).to eq(described_class::DEFAULT_TRACER.call.info) - end - end - - context 'when a custom transport class is specified' do - - let(:transport_class) do - Class.new { def initialize(*); end } - end - - let(:client) do - described_class.new(transport_class: transport_class) - end - - it 'allows the custom transport class to be defined' do - expect(client.transport).to be_a(transport_class) - end - end - - context 'when a custom transport instance is specified' do - - let(:transport_instance) do - Class.new { def initialize(*); end }.new - end - - let(:client) do - described_class.new(transport: transport_instance) - end - - it 'allows the custom transport class to be defined' do - expect(client.transport).to be(transport_instance) - end - end - - context 'when \'transport_options\' are defined' do - - let(:client) do - described_class.new(transport_options: { request: { timeout: 1 } }) - end - - it 'sets the options on the transport' do - expect(client.transport.options[:transport_options][:request]).to eq(timeout: 1) - end - end - - context 'when \'request_timeout\' is defined' do - let(:client) do - described_class.new(request_timeout: 120) - end - - it 'sets the options on the transport' do - expect(client.transport.options[:transport_options][:request]).to eq(timeout: 120) - end - end - - context 'when \'request_timeout\' is defined as a String key' do - let(:client) do - described_class.new('request_timeout' => 120) - end - - it 'sets the options on the transport' do - expect(client.transport.options[:transport_options][:request]).to eq(timeout: 120) - end - end - end - - describe '#perform_request' do - let(:transport_instance) do - Class.new { def initialize(*); end }.new - end - - let(:client) do - described_class.new(transport: transport_instance) - end - - it 'delegates performing requests to the transport' do - expect(transport_instance).to receive(:perform_request).and_return(true) - expect(client.perform_request('GET', '/')).to be(true) - end - - context 'when the \'send_get_body_as\' option is specified' do - let(:client) do - described_class.new(transport: transport_instance, :send_get_body_as => 'POST') - end - - before do - expect(transport_instance).to receive(:perform_request) - .with( - 'POST', '/', {}, - '{"foo":"bar"}', - '{"Content-Type":"application/x-ndjson"}' - ).and_return(true) - end - - let(:request) do - client.perform_request('POST', '/', {}, '{"foo":"bar"}', '{"Content-Type":"application/x-ndjson"}') - end - - it 'sets the option' do - expect(request).to be(true) - end - end - - context 'when Elasticsearch response includes a warning header' do - let(:client) do - Elasticsearch::Transport::Client.new(hosts: hosts) - end - - let(:warning) { 'Elasticsearch warning: "deprecation warning"' } - - it 'prints a warning' do - allow_any_instance_of(Elasticsearch::Transport::Transport::Response).to receive(:headers) do - { 'warning' => warning } - end - - begin - stderr = $stderr - fake_stderr = StringIO.new - $stderr = fake_stderr - - client.perform_request('GET', '/') - fake_stderr.rewind - expect(fake_stderr.string).to eq("warning: #{warning}\n") - ensure - $stderr = stderr - end - end - end - end - - context 'when the client connects to Elasticsearch' do - let(:logger) do - Logger.new(STDERR).tap do |logger| - logger.formatter = proc do |severity, datetime, progname, msg| - color = case severity - when /INFO/ then :green - when /ERROR|WARN|FATAL/ then :red - when /DEBUG/ then :cyan - else :white - end - ANSI.ansi(severity[0] + ' ', color, :faint) + ANSI.ansi(msg, :white, :faint) + "\n" - end - end unless ENV['QUIET'] - end - - let(:port) do - TEST_PORT - end - - let(:transport_options) do - {} - end - - let(:options) do - {} - end - - let(:client) do - described_class.new({ host: hosts, logger: logger }.merge!(transport_options: transport_options).merge!(options)) - end - - context 'when a request is made' do - let!(:response) do - client.perform_request('GET', '_cluster/health') - end - - it 'connects to the cluster' do - expect(response.body['number_of_nodes']).to be >= (1) - end - end - - describe '#initialize' do - context 'when options are specified' do - let(:transport_options) do - { headers: { accept: 'application/yaml', content_type: 'application/yaml' } } - end - - let(:response) do - client.perform_request('GET', '_cluster/health') - end - - it 'applies the options to the client' do - expect(response.body).to match(/---\n/) - expect(response.headers['content-type']).to eq('application/yaml') - end - end - - context 'when a block is provided' do - let(:client) do - Elasticsearch::Client.new(host: ELASTICSEARCH_HOSTS.first, logger: logger) do |client| - client.headers['Accept'] = 'application/yaml' - end - end - - let(:response) do - client.perform_request('GET', '_cluster/health') - end - - it 'executes the block' do - expect(response.body).to match(/---\n/) - expect(response.headers['content-type']).to eq('application/yaml') - end - - context 'when the Faraday adapter is set in the block' do - let(:client) do - Elasticsearch::Client.new(host: ELASTICSEARCH_HOSTS.first, logger: logger) do |client| - client.adapter(:net_http_persistent) - end - end - - let(:handler_name) do - client.transport.connections.first.connection.builder.adapter.name - end - - let(:response) do - client.perform_request('GET', '_cluster/health') - end - - it 'sets the adapter' do - expect(handler_name).to eq('Faraday::Adapter::NetHttpPersistent') - end - - it 'uses the adapter to connect' do - expect(response.status).to eq(200) - end - end - end - end - - describe '#options' do - context 'when retry_on_failure is true' do - context 'when a node is unreachable' do - let(:hosts) do - [ELASTICSEARCH_HOSTS.first, "foobar1", "foobar2"] - end - - let(:options) do - { retry_on_failure: true } - end - - let(:responses) do - 5.times.collect do - client.perform_request('GET', '_nodes/_local') - end - end - - it 'retries on failure' do - expect(responses.all? { true }).to be(true) - end - end - end - - context 'when retry_on_failure is an integer' do - - let(:hosts) do - [ELASTICSEARCH_HOSTS.first, 'foobar1', 'foobar2', 'foobar3'] - end - - let(:options) do - { retry_on_failure: 1 } - end - - it 'retries only the specified number of times' do - expect(client.perform_request('GET', '_nodes/_local')) - expect { - client.perform_request('GET', '_nodes/_local') - }.to raise_exception(Faraday::ConnectionFailed) - end - end - - context 'when reload_on_failure is true' do - - let(:hosts) do - [ELASTICSEARCH_HOSTS.first, 'foobar1', 'foobar2'] - end - - let(:options) do - { reload_on_failure: true } - end - - let(:responses) do - 5.times.collect do - client.perform_request('GET', '_nodes/_local') - end - end - - it 'reloads the connections' do - expect(client.transport.connections.size).to eq(3) - expect(responses.all? { true }).to be(true) - expect(client.transport.connections.size).to be >= (1) - end - end - - context 'when retry_on_status is specified' do - - let(:options) do - { retry_on_status: 400 } - end - - let(:logger) do - double('logger', :debug? => false, :warn? => true, :fatal? => false, :error? => false) - end - - before do - expect(logger).to receive(:warn).exactly(4).times - end - - it 'retries when the status matches' do - expect { - client.perform_request('PUT', '_foobar') - }.to raise_exception(Elasticsearch::Transport::Transport::Errors::BadRequest) - end - end - - context 'when the \'compression\' option is set to true' do - - context 'when using Faraday as the transport' do - - context 'when using the Net::HTTP adapter' do - - let(:client) do - described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :net_http) - end - - it 'compresses the request and decompresses the response' do - expect(client.perform_request('GET', '/').body).to be_a(Hash) - end - - it 'sets the Accept-Encoding header' do - expect(client.transport.connections[0].connection.headers['Accept-Encoding']) - end - - it 'preserves the other headers' do - expect(client.transport.connections[0].connection.headers['User-Agent']) - end - end - - context 'when using the HTTPClient adapter' do - - let(:client) do - described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :httpclient, enable_meta_header: false) - end - - it 'compresses the request and decompresses the response' do - expect(client.perform_request('GET', '/').body).to be_a(Hash) - end - - it 'sets the Accept-Encoding header' do - expect(client.transport.connections[0].connection.headers['Accept-Encoding']) - end - - it 'preserves the other headers' do - expect(client.transport.connections[0].connection.headers['User-Agent']) - end - end - - context 'when using the Patron adapter', unless: jruby? do - let(:client) do - described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :patron) - end - - it 'compresses the request and decompresses the response' do - expect(client.perform_request('GET', '/').body).to be_a(Hash) - end - - it 'sets the Accept-Encoding header' do - expect(client.transport.connections[0].connection.headers['Accept-Encoding']) - end - - it 'preserves the other headers' do - expect(client.transport.connections[0].connection.headers['User-Agent']) - end - end - - context 'when using the Net::HTTP::Persistent adapter' do - let(:client) do - described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :net_http_persistent) - end - - it 'compresses the request and decompresses the response' do - expect(client.perform_request('GET', '/').body).to be_a(Hash) - end - - it 'sets the Accept-Encoding header' do - expect(client.transport.connections[0].connection.headers['Accept-Encoding']) - end - - it 'preserves the other headers' do - expect(client.transport.connections[0].connection.headers['User-Agent']) - end - end - - context 'when using the Typhoeus adapter' do - let(:client) do - described_class.new(hosts: ELASTICSEARCH_HOSTS, compression: true, adapter: :typhoeus) - end - - it 'compresses the request and decompresses the response' do - expect(client.perform_request('GET', '/').body).to be_a(Hash) - end - - it 'sets the Accept-Encoding header' do - expect(client.transport.connections[0].connection.headers['Accept-Encoding']) - end - - it 'preserves the other headers' do - expect(client.transport.connections[0].connection.headers['User-Agent']) - end - end unless jruby? - end - end - - context 'when using Curb as the transport', unless: jruby? do - let(:client) do - described_class.new(hosts: ELASTICSEARCH_HOSTS, - compression: true, - transport_class: Elasticsearch::Transport::Transport::HTTP::Curb) - end - - it 'compresses the request and decompresses the response' do - expect(client.perform_request('GET', '/').body).to be_a(Hash) - end - - it 'sets the Accept-Encoding header' do - expect(client.transport.connections[0].connection.headers['Accept-Encoding']) - end - - it 'preserves the other headers' do - expect(client.transport.connections[0].connection.headers['User-Agent']) - end - end - - context 'when using Manticore as the transport', if: jruby? do - let(:client) do - described_class.new(hosts: ELASTICSEARCH_HOSTS, - compression: true, - transport_class: Elasticsearch::Transport::Transport::HTTP::Manticore) - end - - it 'compresses the request and decompresses the response' do - expect(client.perform_request('GET', '/').body).to be_a(Hash) - end - end - end - - describe '#perform_request' do - context 'when a request is made' do - before do - client.perform_request('DELETE', '_all') - client.perform_request('DELETE', 'myindex') rescue - client.perform_request('PUT', 'myindex', {}, { settings: { number_of_shards: 2, number_of_replicas: 0 } }) - client.perform_request('PUT', 'myindex/_doc/1', { routing: 'XYZ', timeout: '1s' }, { foo: 'bar' }) - client.perform_request('GET', '_cluster/health?wait_for_status=green&timeout=2s', {}) - end - - let(:response) do - client.perform_request('GET', 'myindex/_doc/1?routing=XYZ') - end - - it 'handles paths and URL paramters' do - expect(response.status).to eq(200) - end - - it 'returns response body' do - expect(response.body['_source']).to eq('foo' => 'bar') - end - end - - context 'when an invalid url is specified' do - it 'raises an exception' do - expect { - client.perform_request('GET', 'myindex/_doc/1?routing=FOOBARBAZ') - }.to raise_exception(Elasticsearch::Transport::Transport::Errors::NotFound) - end - end - - context 'when the \'ignore\' parameter is specified' do - - let(:response) do - client.perform_request('PUT', '_foobar', ignore: 400) - end - - it 'exposes the status in the response' do - expect(response.status).to eq(400) - end - - it 'exposes the body of the response' do - expect(response.body).to be_a(Hash) - expect(response.body.inspect).to match(/invalid_index_name_exception/) - end - end - - context 'when request headers are specified' do - - let(:response) do - client.perform_request('GET', '/', {}, nil, { 'Content-Type' => 'application/yaml' }) - end - - it 'passes them to the transport' do - expect(response.body).to match(/---/) - end - end - - describe 'selector' do - - context 'when the round-robin selector is used' do - - let(:nodes) do - 3.times.collect do - client.perform_request('GET', '_nodes/_local').body['nodes'].to_a[0][1]['name'] - end - end - - let(:node_names) do - client.nodes.stats['nodes'].collect do |name, stats| - stats['name'] - end - end - - let(:expected_names) do - 3.times.collect do |i| - node_names[i % node_names.size] - end - end - - # it 'rotates nodes' do - # pending 'Better way to detect rotating nodes' - # expect(nodes).to eq(expected_names) - # end - end - end - - context 'when patron is used as an adapter', unless: jruby? do - before do - require 'patron' - end - - let(:options) do - { adapter: :patron } - end - - let(:adapter) do - client.transport.connections.first.connection.builder.adapter - end - - it 'uses the patron connection handler' do - expect(adapter).to eq('Faraday::Adapter::Patron') - end - - it 'keeps connections open' do - response = client.perform_request('GET', '_nodes/stats/http') - connections_before = response.body['nodes'].values.find { |n| n['name'] == node_names.first }['http']['total_opened'] - client.transport.reload_connections! - response = client.perform_request('GET', '_nodes/stats/http') - connections_after = response.body['nodes'].values.find { |n| n['name'] == node_names.first }['http']['total_opened'] - expect(connections_after).to be >= (connections_before) - end - end - - context 'when typhoeus is used as an adapter', unless: jruby? do - before do - require 'typhoeus' - end - - let(:options) do - { adapter: :typhoeus } - end - - let(:adapter) do - client.transport.connections.first.connection.builder.adapter - end - - it 'uses the patron connection handler' do - expect(adapter).to eq('Faraday::Adapter::Typhoeus') - end - - it 'keeps connections open' do - response = client.perform_request('GET', '_nodes/stats/http') - connections_before = response.body['nodes'].values.find { |n| n['name'] == node_names.first }['http']['total_opened'] - client.transport.reload_connections! - response = client.perform_request('GET', '_nodes/stats/http') - connections_after = response.body['nodes'].values.find { |n| n['name'] == node_names.first }['http']['total_opened'] - expect(connections_after).to be >= (connections_before) - end - end - end - end -end diff --git a/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb b/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb deleted file mode 100644 index 9eca75b85c..0000000000 --- a/elasticsearch-transport/spec/elasticsearch/transport/meta_header_spec.rb +++ /dev/null @@ -1,274 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'spec_helper' - -describe Elasticsearch::Transport::Client do - context 'meta-header' do - let(:subject) { client.transport.connections.first.connection.headers } - let(:client) { described_class.new } - let(:regexp) { /^[a-z]{1,}=[a-z0-9.\-]{1,}(?:,[a-z]{1,}=[a-z0-9._\-]+)*$/ } - let(:adapter) { :net_http } - let(:adapter_code) { "nh=#{defined?(Net::HTTP::VERSION) ? Net::HTTP::VERSION : Net::HTTP::HTTPVersion}" } - let(:meta_header) do - if jruby? - "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION},fd=#{Faraday::VERSION},#{adapter_code}" - else - "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},fd=#{Faraday::VERSION},#{adapter_code}" - end - end - - context 'client_meta_version' do - let(:version) { ['7.1.0-alpha', '7.11.0.pre.1', '8.0.0-beta', '8.0.0.beta.2']} - - it 'converts the version to X.X.Xp' do - expect(client.send(:client_meta_version, '7.0.0-alpha')).to eq('7.0.0p') - expect(client.send(:client_meta_version, '7.11.0.pre.1')).to eq('7.11.0p') - expect(client.send(:client_meta_version, '8.1.0-beta')).to eq('8.1.0p') - expect(client.send(:client_meta_version, '8.0.0.beta.2')).to eq('8.0.0p') - expect(client.send(:client_meta_version, '12.16.4.pre')).to eq('12.16.4p') - end - end - - # We are testing this method in the previous block, so now using it inside the test to make the - # Elasticsearch version in the meta header string dynamic - def meta_version - client.send(:client_meta_version, Elasticsearch::VERSION) - end - - context 'single use of meta header' do - let(:client) do - described_class.new(adapter: adapter).tap do |klient| - allow(klient).to receive(:__build_connections) - end - end - - it 'x-elastic-client-header value matches regexp' do - expect(subject['x-elastic-client-meta']).to match(regexp) - expect(subject).to include('x-elastic-client-meta' => meta_header) - end - end - - context 'when using user-agent headers' do - let(:client) do - transport_options = { headers: { user_agent: 'My Ruby App' } } - described_class.new(transport_options: transport_options, adapter: adapter).tap do |klient| - allow(klient).to receive(:__build_connections) - end - end - - it 'is friendly to previously set headers' do - expect(subject).to include(user_agent: 'My Ruby App') - expect(subject['x-elastic-client-meta']).to match(regexp) - expect(subject).to include('x-elastic-client-meta' => meta_header) - end - end - - context 'adapters' do - let(:meta_header) do - if jruby? - "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION},fd=#{Faraday::VERSION}" - else - "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},fd=#{Faraday::VERSION}" - end - end - let(:client) { described_class.new(adapter: adapter) } - let(:headers) { client.transport.connections.first.connection.headers } - - context 'using net/http/persistent' do - let(:adapter) { :net_http_persistent } - - it 'sets adapter in the meta header version to 0 when not loaded' do - was_required = defined?(Net::HTTP::Persistent) - if was_required - @klass = Net::HTTP::Persistent.clone - Net::HTTP.send(:remove_const, :Persistent) - end - - expect(headers['x-elastic-client-meta']).to match(regexp) - meta = "#{meta_header},np=0" - expect(headers).to include('x-elastic-client-meta' => meta) - - Net::HTTP::Persistent = @klass if was_required - end unless jruby? - - it 'sets adapter in the meta header' do - require 'net/http/persistent' - expect(headers['x-elastic-client-meta']).to match(regexp) - meta = "#{meta_header},np=#{Net::HTTP::Persistent::VERSION}" - expect(headers).to include('x-elastic-client-meta' => meta) - end - end - - context 'using httpclient' do - let(:adapter) { :httpclient } - - it 'sets adapter in the meta header version to 0 when not loaded' do - was_required = defined?(HTTPClient) - if was_required - @klass = HTTPClient.clone - Object.send(:remove_const, :HTTPClient) - end - - expect(headers['x-elastic-client-meta']).to match(regexp) - meta = "#{meta_header},hc=0" - expect(headers).to include('x-elastic-client-meta' => meta) - - HTTPClient = @klass if was_required - end unless jruby? - - it 'sets adapter in the meta header' do - require 'httpclient' - - expect(headers['x-elastic-client-meta']).to match(regexp) - meta = "#{meta_header},hc=#{HTTPClient::VERSION}" - expect(headers).to include('x-elastic-client-meta' => meta) - end - end - - context 'using typhoeus' do - let(:adapter) { :typhoeus } - - it 'sets adapter in the meta header version to 0 when not loaded' do - was_required = defined?(Typhoeus) - if was_required - @klass = Typhoeus.clone - Object.send(:remove_const, :Typhoeus) - end - - expect(headers['x-elastic-client-meta']).to match(regexp) - meta = "#{meta_header},ty=0" - expect(headers).to include('x-elastic-client-meta' => meta) - - Typhoeus = @klass if was_required - end unless jruby? - - it 'sets adapter in the meta header' do - require 'typhoeus' - expect(headers['x-elastic-client-meta']).to match(regexp) - meta = "#{meta_header},ty=#{Typhoeus::VERSION}" - expect(headers).to include('x-elastic-client-meta' => meta) - end - end - - unless jruby? - let(:adapter) { :patron } - - context 'using patron without requiring it' do - it 'sets adapter in the meta header version to 0 when not loaded' do - was_required = defined?(Patron) - if was_required - @klass = Patron.clone - Object.send(:remove_const, :Patron) - end - - expect(headers['x-elastic-client-meta']).to match(regexp) - meta = "#{meta_header},pt=0" - expect(headers).to include('x-elastic-client-meta' => meta) - - Patron = @klass if was_required - end - end - - context 'using patron' do - it 'sets adapter in the meta header' do - require 'patron' - expect(headers['x-elastic-client-meta']).to match(regexp) - meta = "#{meta_header},pt=#{Patron::VERSION}" - expect(headers).to include('x-elastic-client-meta' => meta) - end - end - end - - context 'using other' do - let(:adapter) { :some_other_adapter } - - it 'sets adapter in the meta header without requiring' do - Faraday::Adapter.register_middleware some_other_adapter: Faraday::Adapter::NetHttpPersistent - expect(headers['x-elastic-client-meta']).to match(regexp) - expect(headers).to include('x-elastic-client-meta' => meta_header) - end - - it 'sets adapter in the meta header' do - require 'net/http/persistent' - Faraday::Adapter.register_middleware some_other_adapter: Faraday::Adapter::NetHttpPersistent - expect(headers['x-elastic-client-meta']).to match(regexp) - expect(headers).to include('x-elastic-client-meta' => meta_header) - end - end - end - - if defined?(JRUBY_VERSION) - context 'when using manticore' do - let(:client) do - Elasticsearch::Client.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Manticore) - end - let(:subject) { client.transport.connections.first.connection.instance_variable_get("@options")[:headers]} - - it 'sets manticore in the metaheader' do - expect(subject['x-elastic-client-meta']).to match(regexp) - expect(subject['x-elastic-client-meta']).to match(/mc=[0-9.]+/) - end - end - else - context 'when using curb' do - let(:client) do - Elasticsearch::Client.new(transport_class: Elasticsearch::Transport::Transport::HTTP::Curb) - end - - it 'sets curb in the metaheader' do - expect(subject['x-elastic-client-meta']).to match(regexp) - expect(subject['x-elastic-client-meta']).to match(/cl=[0-9.]+/) - end - end - end - - context 'when using custom transport implementation' do - class MyTransport - include Elasticsearch::Transport::Transport::Base - def initialize(args); end - end - let(:client) { Elasticsearch::Transport::Client.new(transport_class: MyTransport) } - let(:subject) { client.instance_variable_get('@arguments')[:transport_options][:headers] } - let(:meta_header) do - if jruby? - "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION}" - else - "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION}" - end - end - - it 'doesnae set any info about the implementation in the metaheader' do - expect(subject['x-elastic-client-meta']).to match(regexp) - expect(subject).to include('x-elastic-client-meta' => meta_header) - end - end - - context 'when using a different service version' do - before do - stub_const('Elastic::ELASTICSEARCH_SERVICE_VERSION', [:ent, '8.0.0']) - end - - let(:client) { Elasticsearch::Client.new } - - it 'sets the service version in the metaheader' do - expect(subject['x-elastic-client-meta']).to match(regexp) - expect(subject['x-elastic-client-meta']).to start_with('ent=8.0.0') - end - end - end -end diff --git a/elasticsearch-transport/spec/elasticsearch/transport/sniffer_spec.rb b/elasticsearch-transport/spec/elasticsearch/transport/sniffer_spec.rb deleted file mode 100644 index 9435ce3856..0000000000 --- a/elasticsearch-transport/spec/elasticsearch/transport/sniffer_spec.rb +++ /dev/null @@ -1,275 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'spec_helper' - -describe Elasticsearch::Transport::Transport::Sniffer do - let(:transport) do - double('transport').tap do |t| - allow(t).to receive(:perform_request).and_return(response) - allow(t).to receive(:options).and_return(sniffer_timeout: 2) - end - end - - let(:sniffer) do - described_class.new(transport) - end - - let(:response) do - double('response').tap do |r| - allow(r).to receive(:body).and_return(raw_response) - end - end - - let(:raw_response) do - { 'nodes' => { 'n1' => { 'http' => { 'publish_address' => publish_address } } } } - end - - let(:publish_address) do - '127.0.0.1:9250' - end - - describe '#initialize' do - it 'has a transport instance' do - expect(sniffer.transport).to be(transport) - end - - it 'inherits the sniffer timeout from the transport object' do - expect(sniffer.timeout).to eq(2) - end - end - - describe '#timeout' do - let(:sniffer) do - described_class.new(double('transport', options: {})) - end - - before do - sniffer.timeout = 3 - end - - it 'allows the timeout to be configured' do - expect(sniffer.timeout).to eq(3) - end - end - - describe '#hosts' do - let(:hosts) do - sniffer.hosts - end - - context 'when the entire response is parsed' do - let(:raw_response) do - { - "cluster_name" => "elasticsearch_test", - "nodes" => { - "N1" => { - "name" => "Node 1", - "transport_address" => "127.0.0.1:9300", - "host" => "testhost1", - "ip" => "127.0.0.1", - "version" => "7.0.0", - "roles" => [ - "master", - "data", - "ingest" - ], - "attributes" => { - "testattr" => "test" - }, - "http" => { - "bound_address" => [ - "[fe80::1]:9250", - "[::1]:9250", - "127.0.0.1:9250" - ], - "publish_address" => "127.0.0.1:9250", - "max_content_length_in_bytes" => 104857600 - } - } - } - } - end - - it 'parses the id' do - expect(sniffer.hosts[0][:id]).to eq('N1') - end - - it 'parses the name' do - expect(sniffer.hosts[0][:name]).to eq('Node 1') - end - - it 'parses the version' do - expect(sniffer.hosts[0][:version]).to eq('7.0.0') - end - - it 'parses the host' do - expect(sniffer.hosts[0][:host]).to eq('127.0.0.1') - end - - it 'parses the port' do - expect(sniffer.hosts[0][:port]).to eq('9250') - end - - it 'parses the roles' do - expect(sniffer.hosts[0][:roles]).to eq(['master', - 'data', - 'ingest']) - end - - it 'parses the attributes' do - expect(sniffer.hosts[0][:attributes]).to eq('testattr' => 'test') - end - end - - context 'when the transport protocol does not match' do - let(:raw_response) do - { 'nodes' => { 'n1' => { 'foo' => { 'publish_address' => '127.0.0.1:9250' } } } } - end - - it 'does not parse the addresses' do - expect(hosts).to eq([]) - end - end - - context 'when a list of nodes is returned' do - let(:raw_response) do - { 'nodes' => { 'n1' => { 'http' => { 'publish_address' => '127.0.0.1:9250' } }, - 'n2' => { 'http' => { 'publish_address' => '127.0.0.1:9251' } } } } - end - - it 'parses the response' do - expect(hosts.size).to eq(2) - end - - it 'correctly parses the hosts' do - expect(hosts[0][:host]).to eq('127.0.0.1') - expect(hosts[1][:host]).to eq('127.0.0.1') - end - - it 'correctly parses the ports' do - expect(hosts[0][:port]).to eq('9250') - expect(hosts[1][:port]).to eq('9251') - end - end - - context 'when the host and port are an ip address and port' do - it 'parses the response' do - expect(hosts.size).to eq(1) - end - - it 'correctly parses the host' do - expect(hosts[0][:host]).to eq('127.0.0.1') - end - - it 'correctly parses the port' do - expect(hosts[0][:port]).to eq('9250') - end - end - - context 'when the host and port are a hostname and port' do - let(:publish_address) do - 'testhost1.com:9250' - end - - let(:hosts) do - sniffer.hosts - end - - it 'parses the response' do - expect(hosts.size).to eq(1) - end - - it 'correctly parses the host' do - expect(hosts[0][:host]).to eq('testhost1.com') - end - - it 'correctly parses the port' do - expect(hosts[0][:port]).to eq('9250') - end - end - - context 'when the host and port are in the format: hostname/ip:port' do - let(:publish_address) do - 'example.com/127.0.0.1:9250' - end - - it 'parses the response' do - expect(hosts.size).to eq(1) - end - - it 'uses the hostname' do - expect(hosts[0][:host]).to eq('example.com') - end - - it 'correctly parses the port' do - expect(hosts[0][:port]).to eq('9250') - end - - context 'when the address is IPv6' do - let(:publish_address) do - 'example.com/[::1]:9250' - end - - it 'parses the response' do - expect(hosts.size).to eq(1) - end - - it 'uses the hostname' do - expect(hosts[0][:host]).to eq('example.com') - end - - it 'correctly parses the port' do - expect(hosts[0][:port]).to eq('9250') - end - end - end - - context 'when the address is IPv6' do - let(:publish_address) do - '[::1]:9250' - end - - it 'parses the response' do - expect(hosts.size).to eq(1) - end - - it 'correctly parses the host' do - expect(hosts[0][:host]).to eq('::1') - end - - it 'correctly parses the port' do - expect(hosts[0][:port]).to eq('9250') - end - end - - context 'when the transport has :randomize_hosts option' do - let(:raw_response) do - { 'nodes' => { 'n1' => { 'http' => { 'publish_address' => '127.0.0.1:9250' } }, - 'n2' => { 'http' => { 'publish_address' => '127.0.0.1:9251' } } } } - end - - before do - allow(transport).to receive(:options).and_return(randomize_hosts: true) - end - - it 'shuffles the list' do - expect(hosts.size).to eq(2) - end - end - end -end diff --git a/elasticsearch-transport/spec/spec_helper.rb b/elasticsearch-transport/spec/spec_helper.rb deleted file mode 100644 index 31c80bfa36..0000000000 --- a/elasticsearch-transport/spec/spec_helper.rb +++ /dev/null @@ -1,91 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -if ENV['COVERAGE'] && ENV['CI'].nil? - require 'simplecov' - SimpleCov.start { add_filter %r{^/test|spec/} } -end - -require 'elasticsearch' -require 'elasticsearch-transport' -require 'logger' -require 'ansi/code' -require 'hashie/mash' -if defined?(JRUBY_VERSION) - require 'elasticsearch/transport/transport/http/manticore' - require 'pry-nav' -else - require 'pry-byebug' - require 'elasticsearch/transport/transport/http/curb' - require 'curb' -end - -# The hosts to use for creating a elasticsearch client. -# -# @since 7.0.0 -ELASTICSEARCH_HOSTS = if (hosts = ENV['TEST_ES_SERVER'] || ENV['ELASTICSEARCH_HOSTS']) - hosts.split(',').map do |host| - /(http\:\/\/)?(\S+)/.match(host)[2] - end - else - ['localhost:9200'] - end.freeze - -TEST_HOST, TEST_PORT = ELASTICSEARCH_HOSTS.first.split(':') if ELASTICSEARCH_HOSTS - -# Are we testing on JRuby? -# -# @return [ true, false ] Whether JRuby is being used. -# -# @since 7.0.0 -def jruby? - RUBY_PLATFORM =~ /\bjava\b/ -end - -# The names of the connected nodes. -# -# @return [ Array ] The node names. -# -# @since 7.0.0 -def node_names - $node_names ||= default_client.nodes.stats['nodes'].collect do |name, stats| - stats['name'] - end -end - -# The default client. -# -# @return [ Elasticsearch::Client ] The default client. -# -# @since 7.0.0 -def default_client - $client ||= Elasticsearch::Client.new(hosts: ELASTICSEARCH_HOSTS) -end - -module Config - def self.included(context) - # Get the hosts to use to connect an elasticsearch client. - # - # @since 7.0.0 - context.let(:hosts) { ELASTICSEARCH_HOSTS } - end -end - -RSpec.configure do |config| - config.include(Config) - config.formatter = 'documentation' - config.color = true -end diff --git a/elasticsearch-transport/test/integration/jruby_test.rb b/elasticsearch-transport/test/integration/jruby_test.rb deleted file mode 100644 index 37d28ec2f5..0000000000 --- a/elasticsearch-transport/test/integration/jruby_test.rb +++ /dev/null @@ -1,39 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -require 'test_helper' - -if JRUBY - require 'elasticsearch/transport/transport/http/manticore' - - class Elasticsearch::Transport::ClientManticoreIntegrationTest < Minitest::Test - context "Transport" do - setup do - @host, @port = ELASTICSEARCH_HOSTS.first.split(':') - end - - should 'allow to customize the Faraday adapter to Manticore' do - client = Elasticsearch::Transport::Client.new( - transport_class: Elasticsearch::Transport::Transport::HTTP::Manticore, - trace: true, - hosts: [ { host: @host, port: @port } ] - ) - response = client.perform_request 'GET', '' - assert_respond_to(response.body, :to_hash) - end - end - end -end diff --git a/elasticsearch-transport/test/integration/transport_test.rb b/elasticsearch-transport/test/integration/transport_test.rb deleted file mode 100644 index c21992f907..0000000000 --- a/elasticsearch-transport/test/integration/transport_test.rb +++ /dev/null @@ -1,90 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'test_helper' - -class Elasticsearch::Transport::ClientIntegrationTest < Minitest::Test - context "Transport" do - setup do - @host, @port = ELASTICSEARCH_HOSTS.first.split(':') - @hosts = { hosts: [ { host: @host, port: @port } ] } - begin; Object.send(:remove_const, :Patron); rescue NameError; end - end - - should "allow to customize the Faraday adapter to Typhoeus" do - require 'typhoeus' - require 'typhoeus/adapters/faraday' - - transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new(@hosts) do |f| - f.response :logger - f.adapter :typhoeus - end - - client = Elasticsearch::Transport::Client.new transport: transport - client.perform_request 'GET', '' - end unless jruby? - - should "allow to customize the Faraday adapter to NetHttpPersistent" do - require 'net/http/persistent' - - transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new(@hosts) do |f| - f.response :logger - f.adapter :net_http_persistent - end - - client = Elasticsearch::Transport::Client.new transport: transport - client.perform_request 'GET', '' - end - - should "allow to define connection parameters and pass them" do - transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new( - hosts: [ { host: @host, port: @port } ], - options: { transport_options: { params: { :format => 'yaml' } } } - ) - client = Elasticsearch::Transport::Client.new transport: transport - response = client.perform_request 'GET', '' - - assert response.body.start_with?("---\n"), "Response body should be YAML: #{response.body.inspect}" - end - - should "use the Curb client" do - require 'curb' - require 'elasticsearch/transport/transport/http/curb' - transport = Elasticsearch::Transport::Transport::HTTP::Curb.new(@hosts) do |curl| - curl.verbose = true - end - - client = Elasticsearch::Transport::Client.new transport: transport - client.perform_request 'GET', '' - end unless jruby? - - should "deserialize JSON responses in the Curb client" do - require 'curb' - require 'elasticsearch/transport/transport/http/curb' - transport = Elasticsearch::Transport::Transport::HTTP::Curb.new(@hosts) do |curl| - curl.verbose = true - end - - client = Elasticsearch::Transport::Client.new transport: transport - response = client.perform_request 'GET', '' - - assert_respond_to(response.body, :to_hash) - assert_not_nil response.body['name'] - assert_equal 'application/json', response.headers['content-type'] - end unless jruby? - end -end diff --git a/elasticsearch-transport/test/profile/client_benchmark_test.rb b/elasticsearch-transport/test/profile/client_benchmark_test.rb deleted file mode 100644 index 19417a1ee0..0000000000 --- a/elasticsearch-transport/test/profile/client_benchmark_test.rb +++ /dev/null @@ -1,132 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'test_helper' - -class Elasticsearch::Transport::ClientProfilingTest < Minitest::Test - context "Elasticsearch client benchmark" do - setup do - @port = (ENV['TEST_CLUSTER_PORT'] || 9250).to_i - client = Elasticsearch::Client.new host: "localhost:#{@port}", adapter: ::Faraday.default_adapter - client.perform_request 'DELETE', 'ruby_test_benchmark' rescue nil - client.perform_request 'PUT', 'ruby_test_benchmark', {}, {settings: {index: {number_of_shards: 1, number_of_replicas: 0}}} - 100.times do client.perform_request 'POST', 'ruby_test_benchmark_search/test', {}, {foo: 'bar'}; end - client.perform_request 'POST', 'ruby_test_benchmark_search/_refresh' - end - teardown do - client = Elasticsearch::Client.new host: "localhost:#{@port}" - client.perform_request 'DELETE', 'ruby_test_benchmark' rescue nil - client.perform_request 'DELETE', 'ruby_test_benchmark_search' rescue nil - end - - context "with a single-node cluster and the default adapter" do - setup do - @client = Elasticsearch::Client.new hosts: "localhost:#{@port}", adapter: ::Faraday.default_adapter - end - - measure "get the cluster info", count: 1_000 do - @client.perform_request 'GET', '' - end - - measure "index a document" do - @client.perform_request 'POST', 'ruby_test_benchmark/test', {}, {foo: 'bar'} - end - - measure "search" do - @client.perform_request 'GET', 'ruby_test_benchmark_search/test/_search', {}, {query: {match: {foo: 'bar'}}} - end - end - - context "with a two-node cluster and the default adapter" do - setup do - @client = Elasticsearch::Client.new hosts: ["localhost:#{@port}", "localhost:#{@port+1}"], adapter: ::Faraday.default_adapter - end - - measure "get the cluster info", count: 1_000 do - @client.perform_request 'GET', '' - end - - measure "index a document"do - @client.perform_request 'POST', 'ruby_test_benchmark/test', {}, {foo: 'bar'} - end - - measure "search" do - @client.perform_request 'GET', 'ruby_test_benchmark_search/test/_search', {}, {query: {match: {foo: 'bar'}}} - end - end - - context "with a single-node cluster and the Curb client" do - setup do - require 'curb' - require 'elasticsearch/transport/transport/http/curb' - @client = Elasticsearch::Client.new host: "localhost:#{@port}", - transport_class: Elasticsearch::Transport::Transport::HTTP::Curb - end - - measure "get the cluster info", count: 1_000 do - @client.perform_request 'GET', '' - end - - measure "index a document" do - @client.perform_request 'POST', 'ruby_test_benchmark/test', {}, {foo: 'bar'} - end - - measure "search" do - @client.perform_request 'GET', 'ruby_test_benchmark_search/test/_search', {}, {query: {match: {foo: 'bar'}}} - end - end - - context "with a single-node cluster and the Typhoeus client" do - setup do - require 'typhoeus' - require 'typhoeus/adapters/faraday' - @client = Elasticsearch::Client.new host: "localhost:#{@port}", adapter: :typhoeus - end - - measure "get the cluster info", count: 1_000 do - @client.perform_request 'GET', '' - end - - measure "index a document" do - @client.perform_request 'POST', 'ruby_test_benchmark/test', {}, {foo: 'bar'} - end - - measure "search" do - @client.perform_request 'GET', 'ruby_test_benchmark_search/test/_search', {}, {query: {match: {foo: 'bar'}}} - end - end - - context "with a single-node cluster and the Patron adapter" do - setup do - require 'patron' - @client = Elasticsearch::Client.new host: "localhost:#{@port}", adapter: :patron - end - - measure "get the cluster info", count: 1_000 do - @client.perform_request 'GET', '' - end - - measure "index a document" do - @client.perform_request 'POST', 'ruby_test_benchmark/test', {}, {foo: 'bar'} - end - - measure "search" do - @client.perform_request 'GET', 'ruby_test_benchmark_search/test/_search', {}, {query: {match: {foo: 'bar'}}} - end - end - end -end diff --git a/elasticsearch-transport/test/test_helper.rb b/elasticsearch-transport/test/test_helper.rb deleted file mode 100644 index 16e40db51c..0000000000 --- a/elasticsearch-transport/test/test_helper.rb +++ /dev/null @@ -1,88 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - -ELASTICSEARCH_HOSTS = if hosts = ENV['TEST_ES_SERVER'] || ENV['ELASTICSEARCH_HOSTS'] - hosts.split(',').map do |host| - /(http\:\/\/)?(\S+)/.match(host)[2] - end - else - ['localhost:9200'] - end.freeze - -TEST_HOST, TEST_PORT = ELASTICSEARCH_HOSTS.first.split(':') if ELASTICSEARCH_HOSTS - -JRUBY = defined?(JRUBY_VERSION) - -if ENV['COVERAGE'] - require 'simplecov' - SimpleCov.start { add_filter %r{^/test/} } -end - -require 'minitest/autorun' -require 'minitest/reporters' -require 'shoulda/context' -require 'mocha/minitest' -require 'ansi/code' - -require 'require-prof' if ENV["REQUIRE_PROF"] -require 'elasticsearch-transport' -require 'logger' - -require 'hashie' - -RequireProf.print_timing_infos if ENV["REQUIRE_PROF"] - -class FixedMinitestSpecReporter < Minitest::Reporters::SpecReporter - def before_test(test) - last_test = tests.last - - before_suite(test.class) unless last_test - - if last_test && last_test.klass.to_s != test.class.to_s - after_suite(last_test.class) if last_test - before_suite(test.class) - end - end -end - -module Minitest - class Test - def assert_nothing_raised(*args) - begin - line = __LINE__ - yield - rescue RuntimeError => e - raise MiniTest::Assertion, "Exception raised:\n<#{e.class}>", e.backtrace - end - true - end - - def assert_not_nil(object, msg=nil) - msg = message(msg) { "<#{object.inspect}> expected to not be nil" } - assert !object.nil?, msg - end - - def assert_block(*msgs) - assert yield, *msgs - end - - alias :assert_raise :assert_raises - end -end - -Minitest::Reporters.use! FixedMinitestSpecReporter.new diff --git a/elasticsearch-transport/test/unit/connection_test.rb b/elasticsearch-transport/test/unit/connection_test.rb deleted file mode 100644 index 6a0087202b..0000000000 --- a/elasticsearch-transport/test/unit/connection_test.rb +++ /dev/null @@ -1,135 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'test_helper' - -class Elasticsearch::Transport::Transport::Connections::ConnectionTest < Minitest::Test - include Elasticsearch::Transport::Transport::Connections - - context "Connection" do - should "be initialized with :host, :connection, and :options" do - c = Connection.new :host => 'x', :connection => 'y', :options => {} - assert_equal 'x', c.host - assert_equal 'y', c.connection - assert_instance_of Hash, c.options - end - - should "return full path" do - c = Connection.new - assert_equal '_search', c.full_path('_search') - assert_equal '_search', c.full_path('_search', {}) - assert_equal '_search?foo=bar', c.full_path('_search', {:foo => 'bar'}) - assert_equal '_search?foo=bar+bam', c.full_path('_search', {:foo => 'bar bam'}) - end - - should "return full url" do - c = Connection.new :host => { :protocol => 'http', :host => 'localhost', :port => '9200' } - assert_equal 'http://localhost:9200/_search?foo=bar', c.full_url('_search', {:foo => 'bar'}) - end - - should "return full url with credentials" do - c = Connection.new :host => { :protocol => 'http', :user => 'U', :password => 'P', :host => 'localhost', :port => '9200' } - assert_equal 'http://U:P@localhost:9200/_search?foo=bar', c.full_url('_search', {:foo => 'bar'}) - end - - should "return full url with escaped credentials" do - c = Connection.new :host => { :protocol => 'http', :user => 'U$$$', :password => 'P^^^', :host => 'localhost', :port => '9200' } - assert_equal 'http://U%24%24%24:P%5E%5E%5E@localhost:9200/_search?foo=bar', c.full_url('_search', {:foo => 'bar'}) - end - - should "return full url with path" do - c = Connection.new :host => { :protocol => 'http', :host => 'localhost', :port => '9200', :path => '/foo' } - assert_equal 'http://localhost:9200/foo/_search?foo=bar', c.full_url('_search', {:foo => 'bar'}) - end - - should "return right full url with path when path starts with /" do - c = Connection.new :host => { :protocol => 'http', :host => 'localhost', :port => '9200', :path => '/foo' } - assert_equal 'http://localhost:9200/foo/_search?foo=bar', c.full_url('/_search', {:foo => 'bar'}) - end - - should "have a string representation" do - c = Connection.new :host => 'x' - assert_match(/host: x/, c.to_s) - assert_match(/alive/, c.to_s) - end - - should "not be dead by default" do - c = Connection.new - assert ! c.dead? - end - - should "be dead when marked" do - c = Connection.new.dead! - assert c.dead? - assert_equal 1, c.failures - assert_in_delta c.dead_since, Time.now, 1 - end - - should "be alive when marked" do - c = Connection.new.dead! - assert c.dead? - assert_equal 1, c.failures - assert_in_delta c.dead_since, Time.now, 1 - - c.alive! - assert ! c.dead? - assert_equal 1, c.failures - end - - should "be healthy when marked" do - c = Connection.new.dead! - assert c.dead? - assert_equal 1, c.failures - assert_in_delta c.dead_since, Time.now, 1 - - c.healthy! - assert ! c.dead? - assert_equal 0, c.failures - end - - should "be resurrected if timeout passed" do - c = Connection.new.dead! - - now = Time.now + 60 - Time.stubs(:now).returns(now) - - assert c.resurrect!, c.inspect - assert ! c.dead?, c.inspect - end - - should "be resurrected if timeout passed for multiple failures" do - c = Connection.new.dead!.dead! - - now = Time.now + 60*2 - Time.stubs(:now).returns(now) - - assert c.resurrect!, c.inspect - assert ! c.dead?, c.inspect - end - - should "implement the equality operator" do - c1 = Connection.new(:host => { :protocol => 'http', :host => 'foo', :port => 123 }) - c2 = Connection.new(:host => { :protocol => 'http', :host => 'foo', :port => 123 }) - c3 = Connection.new(:host => { :protocol => 'http', :host => 'foo', :port => 456 }) - - assert c1 == c2, "Connection #{c1} should be equal to #{c2}" - assert c2 != c3, "Connection #{c2} should NOT be equal to #{c3}" - end - - end - -end diff --git a/elasticsearch-transport/test/unit/response_test.rb b/elasticsearch-transport/test/unit/response_test.rb deleted file mode 100644 index 31ec5f39ac..0000000000 --- a/elasticsearch-transport/test/unit/response_test.rb +++ /dev/null @@ -1,30 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'test_helper' - -class Elasticsearch::Transport::Transport::ResponseTest < Minitest::Test - context "Response" do - should "force-encode the body into UTF" do - body = "Hello Encoding!".encode(Encoding::ISO_8859_1) - assert_equal 'ISO-8859-1', body.encoding.name - - response = Elasticsearch::Transport::Transport::Response.new 200, body - assert_equal 'UTF-8', response.body.encoding.name - end - end -end diff --git a/elasticsearch-transport/test/unit/serializer_test.rb b/elasticsearch-transport/test/unit/serializer_test.rb deleted file mode 100644 index 3b9743379d..0000000000 --- a/elasticsearch-transport/test/unit/serializer_test.rb +++ /dev/null @@ -1,33 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'test_helper' - -class Elasticsearch::Transport::Transport::SerializerTest < Minitest::Test - - context "Serializer" do - - should "use MultiJson by default" do - ::MultiJson.expects(:load) - ::MultiJson.expects(:dump) - Elasticsearch::Transport::Transport::Serializer::MultiJson.new.load('{}') - Elasticsearch::Transport::Transport::Serializer::MultiJson.new.dump({}) - end - - end - -end diff --git a/elasticsearch-transport/test/unit/transport_base_test.rb b/elasticsearch-transport/test/unit/transport_base_test.rb deleted file mode 100644 index 9e142e3086..0000000000 --- a/elasticsearch-transport/test/unit/transport_base_test.rb +++ /dev/null @@ -1,664 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'test_helper' - -class Elasticsearch::Transport::Transport::BaseTest < Minitest::Test - - class EmptyTransport - include Elasticsearch::Transport::Transport::Base - end - - class DummyTransport - include Elasticsearch::Transport::Transport::Base - def __build_connection(host, options={}, block=nil) - Elasticsearch::Transport::Transport::Connections::Connection.new :host => host, :connection => Object.new - end - end - - class DummyTransportPerformer < DummyTransport - def perform_request(method, path, params={}, body=nil, &block); super; end - end - - class DummySerializer - def initialize(*); end - end - - class DummySniffer - def initialize(*); end - end - - context "Transport::Base" do - should "raise exception when it doesn't implement __build_connection" do - assert_raise NoMethodError do - EmptyTransport.new.__build_connection({ :host => 'foo'}, {}) - end - end - - should "build connections on initialization" do - DummyTransport.any_instance.expects(:__build_connections) - transport = DummyTransport.new - end - - should "have default serializer" do - transport = DummyTransport.new - assert_instance_of Elasticsearch::Transport::Transport::Base::DEFAULT_SERIALIZER_CLASS, transport.serializer - end - - should "have custom serializer" do - transport = DummyTransport.new :options => { :serializer_class => DummySerializer } - assert_instance_of DummySerializer, transport.serializer - - transport = DummyTransport.new :options => { :serializer => DummySerializer.new } - assert_instance_of DummySerializer, transport.serializer - end - - should "have default sniffer" do - transport = DummyTransport.new - assert_instance_of Elasticsearch::Transport::Transport::Sniffer, transport.sniffer - end - - should "have custom sniffer" do - transport = DummyTransport.new :options => { :sniffer_class => DummySniffer } - assert_instance_of DummySniffer, transport.sniffer - end - - context "when combining the URL" do - setup do - @transport = DummyTransport.new - @basic_parts = { :protocol => 'http', :host => 'myhost', :port => 8080 } - end - - should "combine basic parts" do - assert_equal 'http://myhost:8080', @transport.__full_url(@basic_parts) - end - - should "combine path" do - assert_equal 'http://myhost:8080/api', @transport.__full_url(@basic_parts.merge :path => '/api') - end - - should "combine authentication credentials" do - assert_equal 'http://U:P@myhost:8080', @transport.__full_url(@basic_parts.merge :user => 'U', :password => 'P') - end - - should "escape the username and password" do - assert_equal 'http://user%40domain:foo%2Fbar@myhost:8080', - @transport.__full_url(@basic_parts.merge :user => 'user@domain', :password => 'foo/bar') - end - end - end - - context "getting a connection" do - setup do - @transport = DummyTransportPerformer.new :options => { :reload_connections => 5 } - @transport.stubs(:connections).returns(stub :get_connection => Object.new) - @transport.stubs(:sniffer).returns(stub :hosts => []) - end - - should "get a connection" do - assert_not_nil @transport.get_connection - end - - should "increment the counter" do - assert_equal 0, @transport.counter - 3.times { @transport.get_connection } - assert_equal 3, @transport.counter - end - - should "reload connections when it hits the threshold" do - @transport.expects(:reload_connections!).twice - 12.times { @transport.get_connection } - assert_equal 12, @transport.counter - end - - should "not reload connections by default" do - @transport = DummyTransportPerformer.new - @transport.stubs(:connections).returns(stub :get_connection => Object.new) - @transport.expects(:reload_connections!).never - - 10_010.times { @transport.get_connection } - assert_equal 10_010, @transport.counter - end - - should "not reload connections when the option is set to false" do - @transport = DummyTransportPerformer.new :options => { :reload_connections => false } - @transport.stubs(:connections).returns(stub :get_connection => Object.new) - @transport.expects(:reload_connections!).never - - 10_010.times { @transport.get_connection } - assert_equal 10_010, @transport.counter - end - end - - context "performing a request" do - setup do - @transport = DummyTransportPerformer.new - end - - should "raise an error when no block is passed" do - assert_raise NoMethodError do - @transport.peform_request 'GET', '/' - end - end - - should "get the connection" do - @transport.expects(:get_connection).returns(stub_everything :failures => 1) - @transport.perform_request 'GET', '/' do; Elasticsearch::Transport::Transport::Response.new 200, 'OK'; end - end - - should "raise an error when no connection is available" do - @transport.expects(:get_connection).returns(nil) - assert_raise Elasticsearch::Transport::Transport::Error do - @transport.perform_request 'GET', '/' do; Elasticsearch::Transport::Transport::Response.new 200, 'OK'; end - end - end - - should "call the passed block" do - x = 0 - @transport.expects(:get_connection).returns(stub_everything :failures => 1) - - @transport.perform_request 'GET', '/' do |connection, url| - x += 1 - Elasticsearch::Transport::Transport::Response.new 200, 'OK' - end - - assert_equal 1, x - end - - should "deserialize a response JSON body" do - @transport.expects(:get_connection).returns(stub_everything :failures => 1) - @transport.serializer.expects(:load).returns({'foo' => 'bar'}) - - response = @transport.perform_request 'GET', '/' do - Elasticsearch::Transport::Transport::Response.new 200, '{"foo":"bar"}', {"content-type" => 'application/json'} - end - - assert_instance_of Elasticsearch::Transport::Transport::Response, response - assert_equal 'bar', response.body['foo'] - end - - should "not deserialize a response string body" do - @transport.expects(:get_connection).returns(stub_everything :failures => 1) - @transport.serializer.expects(:load).never - response = @transport.perform_request 'GET', '/' do - Elasticsearch::Transport::Transport::Response.new 200, 'FOOBAR', {"content-type" => 'text/plain'} - end - - assert_instance_of Elasticsearch::Transport::Transport::Response, response - assert_equal 'FOOBAR', response.body - end - - should "not deserialize an empty response body" do - @transport.expects(:get_connection).returns(stub_everything :failures => 1) - @transport.serializer.expects(:load).never - response = @transport.perform_request 'GET', '/' do - Elasticsearch::Transport::Transport::Response.new 200, '', {"content-type" => 'application/json'} - end - - assert_instance_of Elasticsearch::Transport::Transport::Response, response - assert_equal '', response.body - end - - should "serialize non-String objects" do - @transport.serializer.expects(:dump).times(3) - @transport.__convert_to_json({:foo => 'bar'}) - @transport.__convert_to_json([1, 2, 3]) - @transport.__convert_to_json(nil) - end - - should "not serialize a String object" do - @transport.serializer.expects(:dump).never - @transport.__convert_to_json('{"foo":"bar"}') - end - - should "raise an error for HTTP status 404" do - @transport.expects(:get_connection).returns(stub_everything :failures => 1) - assert_raise Elasticsearch::Transport::Transport::Errors::NotFound do - @transport.perform_request 'GET', '/' do - Elasticsearch::Transport::Transport::Response.new 404, 'NOT FOUND' - end - end - end - - should "raise an error for HTTP status 404 with application/json content type" do - @transport.expects(:get_connection).returns(stub_everything :failures => 1) - assert_raise Elasticsearch::Transport::Transport::Errors::NotFound do - @transport.perform_request 'GET', '/' do - Elasticsearch::Transport::Transport::Response.new 404, 'NOT FOUND', {"content-type" => 'application/json'} - end - end - end - - should "raise an error on server failure" do - @transport.expects(:get_connection).returns(stub_everything :failures => 1) - assert_raise Elasticsearch::Transport::Transport::Errors::InternalServerError do - @transport.perform_request 'GET', '/' do - Elasticsearch::Transport::Transport::Response.new 500, 'ERROR' - end - end - end - - should "raise an error on connection failure" do - @transport.expects(:get_connection).returns(stub_everything :failures => 1) - - # `block.expects(:call).raises(::Errno::ECONNREFUSED)` fails on Ruby 1.8 - block = lambda { |a,b| raise ::Errno::ECONNREFUSED } - - assert_raise ::Errno::ECONNREFUSED do - @transport.perform_request 'GET', '/', &block - end - end - - should 'raise TooManyRequestsError on 429' do - @transport.expects(:get_connection).returns(stub_everything :failures => 1) - assert_raise Elasticsearch::Transport::Transport::Errors::TooManyRequests do - @transport.perform_request 'GET', '/' do - Elasticsearch::Transport::Transport::Response.new 429, 'ERROR' - end - end - end - - should "not raise an error when the :ignore argument has been passed" do - @transport.stubs(:get_connection).returns(stub_everything :failures => 1) - - assert_raise Elasticsearch::Transport::Transport::Errors::BadRequest do - @transport.perform_request 'GET', '/' do - Elasticsearch::Transport::Transport::Response.new 400, 'CLIENT ERROR' - end - end - - # No `BadRequest` error - @transport.perform_request 'GET', '/', :ignore => 400 do - Elasticsearch::Transport::Transport::Response.new 400, 'CLIENT ERROR' - end - end - - should "mark the connection as dead on failure" do - c = stub_everything :failures => 1 - @transport.expects(:get_connection).returns(c) - - block = lambda { |a,b| raise ::Errno::ECONNREFUSED } - - c.expects(:dead!) - - assert_raise( ::Errno::ECONNREFUSED ) { @transport.perform_request 'GET', '/', &block } - end - end - - context "performing a request with reload connections on connection failures" do - setup do - fake_collection = stub_everything :get_connection => stub_everything(:failures => 1), - :all => stub_everything(:size => 2) - @transport = DummyTransportPerformer.new :options => { :reload_on_failure => 2 } - @transport.stubs(:connections). - returns(fake_collection) - @block = lambda { |c, u| puts "UNREACHABLE" } - end - - should "reload connections when host is unreachable" do - @block.expects(:call).times(2). - raises(Errno::ECONNREFUSED). - then.returns(stub_everything :failures => 1) - - @transport.expects(:reload_connections!).returns([]) - - @transport.perform_request('GET', '/', &@block) - assert_equal 2, @transport.counter - end - end - - context "performing a request with retry on connection failures" do - setup do - @transport = DummyTransportPerformer.new :options => { :retry_on_failure => true } - @transport.stubs(:connections).returns(stub :get_connection => stub_everything(:failures => 1)) - @block = Proc.new { |c, u| puts "UNREACHABLE" } - end - - should "retry DEFAULT_MAX_RETRIES when host is unreachable" do - @block.expects(:call).times(4). - raises(Errno::ECONNREFUSED). - then.raises(Errno::ECONNREFUSED). - then.raises(Errno::ECONNREFUSED). - then.returns(stub_everything :failures => 1) - - assert_nothing_raised do - @transport.perform_request('GET', '/', &@block) - assert_equal 4, @transport.counter - end - end - - should "raise an error after max tries" do - @block.expects(:call).times(4). - raises(Errno::ECONNREFUSED). - then.raises(Errno::ECONNREFUSED). - then.raises(Errno::ECONNREFUSED). - then.raises(Errno::ECONNREFUSED). - then.returns(stub_everything :failures => 1) - - assert_raise Errno::ECONNREFUSED do - @transport.perform_request('GET', '/', &@block) - end - end - end - - context "performing a request with retry on status" do - setup do - DummyTransportPerformer.any_instance.stubs(:connections).returns(stub :get_connection => stub_everything(:failures => 1)) - - logger = Logger.new(STDERR) - logger.level = Logger::DEBUG - DummyTransportPerformer.any_instance.stubs(:logger).returns(logger) - @block = Proc.new { |c, u| puts "ERROR" } - end - - should "not retry when the status code does not match" do - @transport = DummyTransportPerformer.new :options => { :retry_on_status => 500 } - assert_equal [500], @transport.instance_variable_get(:@retry_on_status) - - @block.expects(:call). - returns(Elasticsearch::Transport::Transport::Response.new 400, 'Bad Request'). - times(1) - - @transport.logger. - expects(:warn). - with( regexp_matches(/Attempt \d to get response/) ). - never - - assert_raise Elasticsearch::Transport::Transport::Errors::BadRequest do - @transport.perform_request('GET', '/', {}, nil, &@block) - end - end - - should "retry when the status code does match" do - @transport = DummyTransportPerformer.new :options => { :retry_on_status => 500 } - assert_equal [500], @transport.instance_variable_get(:@retry_on_status) - - @block.expects(:call). - returns(Elasticsearch::Transport::Transport::Response.new 500, 'Internal Error'). - times(4) - - @transport.logger. - expects(:warn). - with( regexp_matches(/Attempt \d to get response/) ). - times(4) - - assert_raise Elasticsearch::Transport::Transport::Errors::InternalServerError do - @transport.perform_request('GET', '/', &@block) - end - end - end - - context "logging" do - setup do - @transport = DummyTransportPerformer.new :options => { :logger => Logger.new('/dev/null') } - - fake_connection = stub :full_url => 'localhost:9200/_search?size=1', - :host => 'localhost', - :connection => stub_everything, - :failures => 0, - :healthy! => true - - @transport.stubs(:get_connection).returns(fake_connection) - @transport.serializer.stubs(:load).returns 'foo' => 'bar' - @transport.serializer.stubs(:dump).returns '{"foo":"bar"}' - end - - should "log the request and response" do - @transport.logger.expects(:info). with do |line| - line =~ %r|POST localhost\:9200/_search\?size=1 \[status\:200, request:.*s, query:n/a\]| - end - @transport.logger.expects(:debug). with '> {"foo":"bar"}' - @transport.logger.expects(:debug). with '< {"foo":"bar"}' - - @transport.perform_request 'POST', '_search', {:size => 1}, {:foo => 'bar'} do - Elasticsearch::Transport::Transport::Response.new 200, '{"foo":"bar"}' - end - end - - should "sanitize password in the URL" do - fake_connection = stub :full_url => 'http://user:password@localhost:9200/_search?size=1', - :host => 'localhost', - :connection => stub_everything, - :failures => 0, - :healthy! => true - @transport.stubs(:get_connection).returns(fake_connection) - - @transport.logger.expects(:info).with do |message| - assert_match(/http:\/\/user:\*{1,15}@localhost\:9200/, message) - true - end - - - @transport.perform_request('GET', '/') {Elasticsearch::Transport::Transport::Response.new 200, '{"foo":"bar"}' } - end - - should "log a failed Elasticsearch request as fatal" do - @block = Proc.new { |c, u| puts "ERROR" } - @block.expects(:call).returns(Elasticsearch::Transport::Transport::Response.new 500, 'ERROR') - - @transport.expects(:__log_response) - @transport.logger.expects(:fatal) - - assert_raise Elasticsearch::Transport::Transport::Errors::InternalServerError do - @transport.perform_request('POST', '_search', &@block) - end - end - - should "not log a failed Elasticsearch request as fatal" do - @block = Proc.new { |c, u| puts "ERROR" } - @block.expects(:call).returns(Elasticsearch::Transport::Transport::Response.new 500, 'ERROR') - - @transport.expects(:__log_response).once - @transport.logger.expects(:fatal).never - - # No `BadRequest` error - @transport.perform_request('POST', '_search', :ignore => 500, &@block) - end - - should "log and re-raise a Ruby exception" do - @block = Proc.new { |c, u| puts "ERROR" } - @block.expects(:call).raises(Exception) - - @transport.expects(:__log_response).never - @transport.logger.expects(:fatal) - - assert_raise(Exception) { @transport.perform_request('POST', '_search', &@block) } - end - end - - context "tracing" do - setup do - @transport = DummyTransportPerformer.new :options => { :tracer => Logger.new('/dev/null') } - - fake_connection = stub :full_url => 'localhost:9200/_search?size=1', - :host => 'localhost', - :connection => stub_everything, - :failures => 0, - :healthy! => true - - @transport.stubs(:get_connection).returns(fake_connection) - @transport.serializer.stubs(:load).returns 'foo' => 'bar' - @transport.serializer.stubs(:dump).returns <<-JSON.gsub(/^ /, '') - { - "foo" : { - "bar" : { - "bam" : true - } - } - } - JSON - end - - should "trace the request" do - @transport.tracer.expects(:info). with do |message| - message == <<-CURL.gsub(/^ /, '') - curl -X POST 'http://localhost:9200/_search?pretty&size=1' -d '{ - "foo" : { - "bar" : { - "bam" : true - } - } - } - ' - CURL - end.once - - @transport.perform_request 'POST', '_search', {:size => 1}, {:q => 'foo'} do - Elasticsearch::Transport::Transport::Response.new 200, '{"foo":"bar"}' - end - end - - should "trace a failed Elasticsearch request" do - @block = Proc.new { |c, u| puts "ERROR" } - @block.expects(:call).returns(Elasticsearch::Transport::Transport::Response.new 500, 'ERROR') - - @transport.expects(:__trace) - - assert_raise Elasticsearch::Transport::Transport::Errors::InternalServerError do - @transport.perform_request('POST', '_search', &@block) - end - end - - end - - context "reloading connections" do - setup do - @transport = DummyTransport.new :options => { :logger => Logger.new('/dev/null') } - end - - should "rebuild connections" do - @transport.sniffer.expects(:hosts).returns([]) - @transport.expects(:__rebuild_connections) - @transport.reload_connections! - end - - should "log error and continue when timing out while sniffing hosts" do - @transport.sniffer.expects(:hosts).raises(Elasticsearch::Transport::Transport::SnifferTimeoutError) - @transport.logger.expects(:error) - assert_nothing_raised do - @transport.reload_connections! - end - end - - should "keep existing connections" do - @transport.__rebuild_connections :hosts => [ { :host => 'node1', :port => 1 } ], :options => { :http => {} } - assert_equal 1, @transport.connections.size - - old_connection_id = @transport.connections.first.object_id - - @transport.__rebuild_connections :hosts => [ { :host => 'node1', :port => 1 }, - { :host => 'node2', :port => 2 } ], - :options => { :http => {} } - - assert_equal 2, @transport.connections.size - assert_equal old_connection_id, @transport.connections.first.object_id - end - - should "remove dead connections" do - @transport.__rebuild_connections :hosts => [ { :host => 'node1', :port => 1 }, - { :host => 'node2', :port => 2 } ], - :options => { :http => {} } - assert_equal 2, @transport.connections.size - - @transport.connections[1].dead! - - @transport.__rebuild_connections :hosts => [ { :host => 'node1', :port => 1 } ], :options => { :http => {} } - - assert_equal 1, @transport.connections.size - assert_equal 1, @transport.connections.all.size - end - - should "not duplicate connections" do - @transport.__rebuild_connections :hosts => [ { :host => 'node1', :port => 1 }, - { :host => 'node2', :port => 2 } ], - :options => { :http => {} } - assert_equal 2, @transport.connections.size - - @transport.connections[0].dead! - - @transport.__rebuild_connections :hosts => [ { :host => 'node1', :port => 1 }, - { :host => 'node2', :port => 2 } ], - :options => { :http => {} } - - assert_equal 2, @transport.connections.all.size - assert_equal 1, @transport.connections.size - end - end - - context "rebuilding connections" do - setup do - @transport = DummyTransport.new - end - - should "close connections" do - @transport.expects(:__close_connections) - @transport.__rebuild_connections :hosts => [ { :scheme => 'http', :host => 'foo', :port => 1 } ], :options => { :http => {} } - end - - should "should replace the connections" do - assert_equal 0, @transport.connections.size - - @transport.__rebuild_connections :hosts => [{ :scheme => 'http', :host => 'foo', :port => 1 }], - :options => { :http => {} } - - assert_equal 1, @transport.connections.size - end - end - - context "resurrecting connections" do - setup do - @transport = DummyTransportPerformer.new - end - - should "delegate to dead connections" do - @transport.connections.expects(:dead).returns([]) - @transport.resurrect_dead_connections! - end - - should "not resurrect connections until timeout" do - @transport.connections.expects(:get_connection).returns(stub_everything :failures => 1).times(5) - @transport.expects(:resurrect_dead_connections!).never - 5.times { @transport.get_connection } - end - - should "resurrect connections after timeout" do - @transport.connections.expects(:get_connection).returns(stub_everything :failures => 1).times(5) - @transport.expects(:resurrect_dead_connections!) - - 4.times { @transport.get_connection } - - now = Time.now + 60*2 - Time.stubs(:now).returns(now) - - @transport.get_connection - end - - should "mark connection healthy if it succeeds" do - c = stub_everything(:failures => 1) - @transport.expects(:get_connection).returns(c) - c.expects(:healthy!) - - @transport.perform_request('GET', '/') { |connection, url| Elasticsearch::Transport::Transport::Response.new 200, 'OK' } - end - end - - context "errors" do - should "raise highest-level Error exception for any ServerError" do - assert_kind_of Elasticsearch::Transport::Transport::Error, Elasticsearch::Transport::Transport::ServerError.new - end - end -end diff --git a/elasticsearch-transport/test/unit/transport_curb_test.rb b/elasticsearch-transport/test/unit/transport_curb_test.rb deleted file mode 100644 index 2ec789075e..0000000000 --- a/elasticsearch-transport/test/unit/transport_curb_test.rb +++ /dev/null @@ -1,135 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'test_helper' - -if JRUBY - puts "'#{File.basename(__FILE__)}' not supported on JRuby #{RUBY_VERSION}" -else - require 'elasticsearch/transport/transport/http/curb' - require 'curb' - - class Elasticsearch::Transport::Transport::HTTP::FaradayTest < Minitest::Test - include Elasticsearch::Transport::Transport::HTTP - - context "Curb transport" do - setup do - @transport = Curb.new :hosts => [ { :host => 'foobar', :port => 1234 } ] - end - - should "implement host_unreachable_exceptions" do - assert_instance_of Array, @transport.host_unreachable_exceptions - end - - should "implement __build_connections" do - assert_equal 1, @transport.hosts.size - assert_equal 1, @transport.connections.size - - assert_instance_of ::Curl::Easy, @transport.connections.first.connection - assert_equal 'http://foobar:1234', @transport.connections.first.connection.url - end - - should "perform the request" do - @transport.connections.first.connection.expects(:http).returns(stub_everything) - @transport.perform_request 'GET', '/' - end - - should "set body for GET request" do - @transport.connections.first.connection.expects(:put_data=).with('{"foo":"bar"}') - @transport.connections.first.connection.expects(:http).with(:GET).returns(stub_everything) - @transport.perform_request 'GET', '/', {}, '{"foo":"bar"}' - end - - should "perform request with headers" do - @transport.connections.first.connection.expects(:put_data=).with('{"foo":"bar"}') - @transport.connections.first.connection.expects(:http).with(:POST).returns(stub_everything) - @transport.connections.first.connection.headers.expects(:merge!).with("Content-Type" => "application/x-ndjson") - - @transport.perform_request 'POST', '/', {}, {:foo => 'bar'}, {"Content-Type" => "application/x-ndjson"} - end - - should "set body for PUT request" do - @transport.connections.first.connection.expects(:put_data=) - @transport.connections.first.connection.expects(:http).with(:PUT).returns(stub_everything) - @transport.perform_request 'PUT', '/', {}, {:foo => 'bar'} - end - - should "serialize the request body" do - @transport.connections.first.connection.expects(:http).with(:POST).returns(stub_everything) - @transport.serializer.expects(:dump) - @transport.perform_request 'POST', '/', {}, {:foo => 'bar'} - end - - should "not serialize a String request body" do - @transport.connections.first.connection.expects(:http).with(:POST).returns(stub_everything) - @transport.serializer.expects(:dump).never - @transport.perform_request 'POST', '/', {}, '{"foo":"bar"}' - end - - should "set application/json response header" do - @transport.connections.first.connection.expects(:http).with(:GET).returns(stub_everything) - @transport.connections.first.connection.expects(:body_str).returns('{"foo":"bar"}') - @transport.connections.first.connection.expects(:header_str).returns('HTTP/1.1 200 OK\r\nContent-Type: application/json; charset=UTF-8\r\nContent-Length: 311\r\n\r\n') - - response = @transport.perform_request 'GET', '/' - - assert_equal 'application/json', response.headers['content-type'] - end - - should "handle HTTP methods" do - @transport.connections.first.connection.expects(:http).with(:HEAD).returns(stub_everything) - @transport.connections.first.connection.expects(:http).with(:GET).returns(stub_everything) - @transport.connections.first.connection.expects(:http).with(:PUT).returns(stub_everything) - @transport.connections.first.connection.expects(:http).with(:POST).returns(stub_everything) - @transport.connections.first.connection.expects(:http).with(:DELETE).returns(stub_everything) - - %w| HEAD GET PUT POST DELETE |.each { |method| @transport.perform_request method, '/' } - - assert_raise(ArgumentError) { @transport.perform_request 'FOOBAR', '/' } - end - - should "properly pass the Content-Type header option" do - transport = Curb.new :hosts => [ { :host => 'foobar', :port => 1234 } ], :options => { :transport_options => { :headers => { 'Content-Type' => 'foo/bar' } } } - - assert_equal "foo/bar", transport.connections.first.connection.headers["Content-Type"] - end - - should "allow to set options for Curb" do - transport = Curb.new :hosts => [ { :host => 'foobar', :port => 1234 } ] do |curl| - curl.headers["User-Agent"] = "myapp-0.0" - end - - assert_equal "myapp-0.0", transport.connections.first.connection.headers["User-Agent"] - end - - should "set the credentials if passed" do - transport = Curb.new :hosts => [ { :host => 'foobar', :port => 1234, :user => 'foo', :password => 'bar' } ] - assert_equal 'foo', transport.connections.first.connection.username - assert_equal 'bar', transport.connections.first.connection.password - end - - should "use global http configuration" do - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234 } ], - :options => { :http => { :scheme => 'https', :user => 'U', :password => 'P' } } - - assert_equal 'https://U:P@foobar:1234/', transport.connections.first.full_url('') - end - end - - end - -end diff --git a/elasticsearch-transport/test/unit/transport_faraday_test.rb b/elasticsearch-transport/test/unit/transport_faraday_test.rb deleted file mode 100644 index 9791e0ce24..0000000000 --- a/elasticsearch-transport/test/unit/transport_faraday_test.rb +++ /dev/null @@ -1,228 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'test_helper' - -class Elasticsearch::Transport::Transport::HTTP::FaradayTest < Minitest::Test - include Elasticsearch::Transport::Transport::HTTP - - context "Faraday transport" do - setup do - @transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234 } ] - end - - should "implement host_unreachable_exceptions" do - assert_instance_of Array, @transport.host_unreachable_exceptions - end - - should "implement __build_connections" do - assert_equal 1, @transport.hosts.size - assert_equal 1, @transport.connections.size - - assert_instance_of ::Faraday::Connection, @transport.connections.first.connection - assert_equal 'http://foobar:1234/', @transport.connections.first.connection.url_prefix.to_s - end - - should "perform the request" do - @transport.connections.first.connection.expects(:run_request).returns(stub_everything) - @transport.perform_request 'GET', '/' - end - - should "return a Response" do - @transport.connections.first.connection.expects(:run_request).returns(stub_everything) - response = @transport.perform_request 'GET', '/' - assert_instance_of Elasticsearch::Transport::Transport::Response, response - end - - should "properly prepare the request" do - @transport.connections.first.connection.expects(:run_request).with do |method, url, body, headers| - assert_equal :post, method - assert_equal '{"foo":"bar"}', body - assert_nil headers['Accept'] - true - end.returns(stub_everything) - - @transport.perform_request 'POST', '/', {}, {:foo => 'bar'} - end - - should "properly prepare the request with custom headers" do - @transport.connections.first.connection.expects(:run_request).with do |method, url, body, headers| - assert_equal :post, method - assert_equal '{"foo":"bar"}', body - assert_nil headers['Accept'] - assert_equal "application/x-ndjson", headers['Content-Type'] - true - end.returns(stub_everything) - - @transport.perform_request 'POST', '/', {}, {:foo => 'bar'}, {"Content-Type" => "application/x-ndjson"} - end - - should "properly pass the Content-Type header option" do - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234 } ], :options => { :transport_options => { :headers => { 'Content-Type' => 'foo/bar' } } } - - transport.connections.first.connection.expects(:run_request).with do |method, url, body, headers| - assert_equal 'foo/bar', headers['Content-Type'] - true - end.returns(stub_everything) - - transport.perform_request 'GET', '/' - end - - should "serialize the request body" do - @transport.connections.first.connection.expects(:run_request).returns(stub_everything) - @transport.serializer.expects(:dump) - @transport.perform_request 'POST', '/', {}, {:foo => 'bar'} - end - - should "not serialize a String request body" do - @transport.connections.first.connection.expects(:run_request).returns(stub_everything) - @transport.serializer.expects(:dump).never - @transport.perform_request 'POST', '/', {}, '{"foo":"bar"}' - end - - should "pass the selector_class options to collection" do - @transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234 } ], - :options => { :selector_class => Elasticsearch::Transport::Transport::Connections::Selector::Random } - assert_instance_of Elasticsearch::Transport::Transport::Connections::Selector::Random, - @transport.connections.selector - end - - should "pass the selector option to collection" do - @transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234 } ], - :options => { :selector => Elasticsearch::Transport::Transport::Connections::Selector::Random.new } - assert_instance_of Elasticsearch::Transport::Transport::Connections::Selector::Random, - @transport.connections.selector - end - - should "pass a configuration block to the Faraday constructor" do - config_block = lambda do |f| - f.response :logger - f.path_prefix = '/moo' - end - - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234 } ], &config_block - - handlers = transport.connections.first.connection.builder.handlers - - assert_equal 1, handlers.size - assert handlers.include?(::Faraday::Response::Logger), "#{handlers.inspect} does not include <::Faraday::Adapter::Logger>" - - assert_equal '/moo', transport.connections.first.connection.path_prefix - assert_equal 'http://foobar:1234/moo', transport.connections.first.connection.url_prefix.to_s - end - - should "pass transport_options to the Faraday constructor" do - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234 } ], - :options => { :transport_options => { - :request => { :open_timeout => 1 }, - :headers => { :foo_bar => 'bar' }, - :ssl => { :verify => false } - } - } - - assert_equal 1, transport.connections.first.connection.options.open_timeout - assert_equal 'bar', transport.connections.first.connection.headers['Foo-Bar'] - assert_equal false, transport.connections.first.connection.ssl.verify? - end - - should "merge in parameters defined in the Faraday connection parameters" do - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234 } ], - :options => { :transport_options => { - :params => { :format => 'yaml' } - } - } - # transport.logger = Logger.new(STDERR) - - transport.connections.first.connection.expects(:run_request). - with do |method, url, params, body| - assert_match(/\?format=yaml/, url) - true - end. - returns(stub_everything) - - transport.perform_request 'GET', '' - end - - should "not overwrite request parameters with the Faraday connection parameters" do - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234 } ], - :options => { :transport_options => { - :params => { :format => 'yaml' } - } - } - # transport.logger = Logger.new(STDERR) - - transport.connections.first.connection.expects(:run_request). - with do |method, url, params, body| - assert_match(/\?format=json/, url) - true - end. - returns(stub_everything) - - transport.perform_request 'GET', '', { :format => 'json' } - end - - should "set the credentials if passed" do - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234, :user => 'foo', :password => 'bar' } ] - assert_equal 'Basic Zm9vOmJhcg==', transport.connections.first.connection.headers['Authorization'] - end - - should "set the credentials if they exist in options" do - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234 } ], - :options => { :user => 'foo', :password => 'bar' } - assert_equal 'Basic Zm9vOmJhcg==', transport.connections.first.connection.headers['Authorization'] - end - - should "override options credentials if passed explicitly" do - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234, :user => 'foo', :password => 'bar' }, - { :host => 'foobar2', :port => 1234 } ], - :options => { :user => 'foo2', :password => 'bar2' } - assert_equal 'Basic Zm9vOmJhcg==', transport.connections.first.connection.headers['Authorization'] - assert_equal 'Basic Zm9vMjpiYXIy', transport.connections[1].connection.headers['Authorization'] - end - - should "set connection scheme to https if passed" do - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234, :scheme => 'https' } ] - - assert_instance_of ::Faraday::Connection, transport.connections.first.connection - assert_equal 'https://foobar:1234/', transport.connections.first.connection.url_prefix.to_s - end - - should "set connection scheme to https if it exist in options" do - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234} ], - :options => { :scheme => 'https' } - - assert_instance_of ::Faraday::Connection, transport.connections.first.connection - assert_equal 'https://foobar:1234/', transport.connections.first.connection.url_prefix.to_s - end - - should "override options scheme if passed explicitly" do - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234, :scheme => 'http'} ], - :options => { :scheme => 'https' } - - assert_instance_of ::Faraday::Connection, transport.connections.first.connection - assert_equal 'http://foobar:1234/', transport.connections.first.connection.url_prefix.to_s - end - - should "use global http configuration" do - transport = Faraday.new :hosts => [ { :host => 'foobar', :port => 1234 } ], - :options => { :http => { :scheme => 'https', :user => 'U', :password => 'P' } } - - assert_equal 'https://U:P@foobar:1234/', transport.connections.first.full_url('') - end - end - -end diff --git a/elasticsearch-transport/test/unit/transport_manticore_test.rb b/elasticsearch-transport/test/unit/transport_manticore_test.rb deleted file mode 100644 index 5250b1dfd7..0000000000 --- a/elasticsearch-transport/test/unit/transport_manticore_test.rb +++ /dev/null @@ -1,251 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'test_helper' - -if JRUBY - require 'elasticsearch/transport/transport/http/manticore' - require 'manticore' - - module Elasticsearch - module Transport - module Transport - module HTTP - class ManticoreTest < Minitest::Test - include Elasticsearch::Transport::Transport::HTTP - - def common_headers - { - 'Content-Type' => 'application/json', - 'User-Agent' => @transport.send(:user_agent_header) - } - end - - context 'Manticore transport' do - setup do - @transport = Manticore.new(hosts: [{ host: '127.0.0.1', port: 8080 }]) - end - - should 'implement host_unreachable_exceptions' do - assert_instance_of Array, @transport.host_unreachable_exceptions - end - - should 'implement __build_connections' do - assert_equal 1, @transport.hosts.size - assert_equal 1, @transport.connections.size - - assert_instance_of(::Manticore::Client, @transport.connections.first.connection) - end - - should 'not close connections in __close_connections' do - assert_equal 1, @transport.connections.size - @transport.__close_connections - assert_equal 1, @transport.connections.size - end - - should 'perform the request' do - @transport.connections.first.connection.expects(:get).returns(stub_everything) - response = @transport.perform_request('GET', '/') - end - - should 'set body for GET request' do - @transport.connections.first.connection.expects(:get) - .with( - 'http://127.0.0.1:8080/', - { - body: '{"foo":"bar"}', - headers: common_headers - } - ).returns(stub_everything) - @transport.perform_request 'GET', '/', {}, '{"foo":"bar"}' - end - - should 'set body for PUT request' do - @transport.connections.first.connection.expects(:put) - .with( - 'http://127.0.0.1:8080/', - { - body: '{"foo":"bar"}', - headers: { - 'Content-Type' => 'application/json', - 'User-Agent' => @transport.send(:user_agent_header) - } - } - ).returns(stub_everything) - @transport.perform_request 'PUT', '/', {}, { foo: 'bar' } - end - - should 'serialize the request body' do - @transport.connections.first.connection.expects(:post) - .with( - 'http://127.0.0.1:8080/', - { - body: '{"foo":"bar"}', - headers: { - 'Content-Type' => 'application/json', - 'User-Agent' => @transport.send(:user_agent_header) - } - } - ).returns(stub_everything) - @transport.perform_request 'POST', '/', {}, { 'foo' => 'bar' } - end - - should 'set custom headers for PUT request' do - @transport.connections.first.connection.expects(:put) - .with( - 'http://127.0.0.1:8080/', - { - body: '{"foo":"bar"}', - headers: { - 'Content-Type' => 'application/json', - 'User-Agent' => @transport.send(:user_agent_header) - } - } - ).returns(stub_everything) - @transport.perform_request 'PUT', '/', {}, '{"foo":"bar"}', { 'Content-Type' => 'application/x-ndjson' } - end - - should 'not serialize a String request body' do - @transport.connections.first.connection.expects(:post) - .with( - 'http://127.0.0.1:8080/', - { - body: '{"foo":"bar"}', - headers: { - 'Content-Type' => 'application/json', - 'User-Agent' => @transport.send(:user_agent_header) - } - } - ).returns(stub_everything) - @transport.serializer.expects(:dump).never - @transport.perform_request 'POST', '/', {}, '{"foo":"bar"}' - end - - should 'set application/json header' do - options = { - headers: { 'content-type' => 'application/json' } - } - - transport = Manticore.new(hosts: [{ host: 'localhost', port: 8080 }], options: options) - transport.connections.first.connection.stub( - 'http://localhost:8080/', - body: '""', - headers: { - 'Content-Type' => 'application/x-ndjson', - 'User-Agent' => @transport.send(:user_agent_header) - }, - code: 200 - ) - response = transport.perform_request('GET', '/', {}) - assert_equal response.status, 200 - end - - should "set headers from 'transport_options'" do - options = { - transport_options: { - headers: { 'Content-Type' => 'foo/bar' } - } - } - - transport = Manticore.new(hosts: [{ host: 'localhost', port: 8080 }], options: options) - - assert_equal( - 'foo/bar', - transport.connections.first.connection.instance_variable_get(:@options)[:headers]['Content-Type'] - ) - # TODO: Needs to check @request_options - end - - should 'handle HTTP methods' do - @transport.connections.first.connection.expects(:delete).with('http://127.0.0.1:8080/', { headers: common_headers }).returns(stub_everything) - @transport.connections.first.connection.expects(:head).with('http://127.0.0.1:8080/', { headers: common_headers }).returns(stub_everything) - @transport.connections.first.connection.expects(:get).with('http://127.0.0.1:8080/', { headers: common_headers }).returns(stub_everything) - @transport.connections.first.connection.expects(:put).with('http://127.0.0.1:8080/', { headers: common_headers }).returns(stub_everything) - @transport.connections.first.connection.expects(:post).with('http://127.0.0.1:8080/', { headers: common_headers }).returns(stub_everything) - - %w[HEAD GET PUT POST DELETE].each { |method| @transport.perform_request method, '/' } - - assert_raise(ArgumentError) { @transport.perform_request 'FOOBAR', '/' } - end - - should 'allow to set options for Manticore' do - options = { headers: { 'User-Agent' => 'myapp-0.0' } } - transport = Manticore.new(hosts: [{ host: 'foobar', port: 1234 }], options: options) - transport.connections.first.connection - .expects(:get) - .with do |_host, _options| - assert_equal 'myapp-0.0', _options[:headers]['User-Agent'] - true - end - .returns(stub_everything) - - transport.perform_request 'GET', '/', {} - end - - should 'allow to set ssl options for Manticore' do - options = { - ssl: { - truststore: 'test.jks', - truststore_password: 'test', - verify: false - } - } - - ::Manticore::Client.expects(:new).with(options) - transport = Manticore.new hosts: [{ host: 'foobar', port: 1234 }], options: options - end - - should 'allow custom headers' do - transport_options = { headers: { 'Authorization' => 'Basic token' } } - transport = Manticore.new( - hosts: [{ host: 'foobar', port: 1234 }], - transport_options: transport_options - ) - - assert_equal( - transport.instance_variable_get(:@request_options)[:headers]['Authorization'], - 'Basic token' - ) - transport.connections.first.connection - .expects(:get) - .with do |_host, _options| - assert_equal('Basic token', _options[:headers]['Authorization']) - true - end - .returns(stub_everything) - - transport.perform_request('GET', '/', {}) - end - - should 'pass :transport_options to Manticore::Client' do - options = { - transport_options: { potatoes: 1 } - } - - ::Manticore::Client.expects(:new).with(potatoes: 1, ssl: {}) - transport = Manticore.new(hosts: [{ host: 'foobar', port: 1234 }], options: options) - end - end - end - end - end - end - end -else - version = "#{defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'Ruby'} #{RUBY_VERSION}" - puts "SKIP: '#{File.basename(__FILE__)}' only supported on JRuby (you're running #{version})" -end From 29964db2ce13987a42284556be849d503f4246e7 Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Thu, 16 Sep 2021 09:32:19 +0100 Subject: [PATCH 02/11] [GEM] Updates elasticsearch gem for elastic-transport --- elasticsearch/Gemfile | 4 ---- elasticsearch/elasticsearch.gemspec | 4 ++-- elasticsearch/lib/elasticsearch.rb | 4 ++-- elasticsearch/spec/integration/client_integration_spec.rb | 4 +++- elasticsearch/spec/unit/api_key_spec.rb | 4 ++-- .../spec/unit/custom_transport_implementation_spec.rb | 6 +++--- 6 files changed, 12 insertions(+), 14 deletions(-) diff --git a/elasticsearch/Gemfile b/elasticsearch/Gemfile index 0bca517d76..e22add2629 100644 --- a/elasticsearch/Gemfile +++ b/elasticsearch/Gemfile @@ -23,7 +23,3 @@ gemspec if File.exist? File.expand_path('../../elasticsearch-api/elasticsearch-api.gemspec', __FILE__) gem 'elasticsearch-api', path: File.expand_path('../../elasticsearch-api', __FILE__), require: false end - -if File.exist? File.expand_path('../../elasticsearch-transport/elasticsearch-transport.gemspec', __FILE__) - gem 'elasticsearch-transport', path: File.expand_path('../../elasticsearch-transport', __FILE__), require: false -end diff --git a/elasticsearch/elasticsearch.gemspec b/elasticsearch/elasticsearch.gemspec index d194c5324e..48b6bc57bb 100644 --- a/elasticsearch/elasticsearch.gemspec +++ b/elasticsearch/elasticsearch.gemspec @@ -45,8 +45,8 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 2.5' - s.add_dependency 'elasticsearch-transport', '8.0.0' - s.add_dependency 'elasticsearch-api', '8.0.0' + s.add_dependency 'elastic-transport', '8.0.0.pre1' + s.add_dependency 'elasticsearch-api', '8.0.0' s.add_development_dependency 'bundler' s.add_development_dependency 'byebug' unless defined?(JRUBY_VERSION) || defined?(Rubinius) diff --git a/elasticsearch/lib/elasticsearch.rb b/elasticsearch/lib/elasticsearch.rb index d407394b3e..66a504fdd1 100644 --- a/elasticsearch/lib/elasticsearch.rb +++ b/elasticsearch/lib/elasticsearch.rb @@ -16,7 +16,7 @@ # under the License. require 'elasticsearch/version' -require 'elasticsearch/transport' +require 'elastic/transport' require 'elasticsearch/api' module Elasticsearch @@ -48,7 +48,7 @@ def initialize(arguments = {}, &block) arguments[:port] ) end - @transport = Elasticsearch::Transport::Client.new(arguments, &block) + @transport = Elastic::Transport::Client.new(arguments, &block) end def method_missing(name, *args, &block) diff --git a/elasticsearch/spec/integration/client_integration_spec.rb b/elasticsearch/spec/integration/client_integration_spec.rb index d0f07b9471..d8294976e9 100644 --- a/elasticsearch/spec/integration/client_integration_spec.rb +++ b/elasticsearch/spec/integration/client_integration_spec.rb @@ -26,7 +26,9 @@ let(:client) do Elasticsearch::Client.new( host: ELASTICSEARCH_URL, - logger: logger + logger: logger, + user: 'elastic', + password: 'changeme' ) end diff --git a/elasticsearch/spec/unit/api_key_spec.rb b/elasticsearch/spec/unit/api_key_spec.rb index 5df47bd76d..2c26d1fb2e 100644 --- a/elasticsearch/spec/unit/api_key_spec.rb +++ b/elasticsearch/spec/unit/api_key_spec.rb @@ -76,9 +76,9 @@ let(:adapter_code) { "nh=#{defined?(Net::HTTP::VERSION) ? Net::HTTP::VERSION : Net::HTTP::HTTPVersion}" } let(:meta_header) do if jruby? - "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION},fd=#{Faraday::VERSION},#{adapter_code}" + "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION},fd=#{Faraday::VERSION},#{adapter_code}" else - "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},fd=#{Faraday::VERSION},#{adapter_code}" + "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},fd=#{Faraday::VERSION},#{adapter_code}" end end diff --git a/elasticsearch/spec/unit/custom_transport_implementation_spec.rb b/elasticsearch/spec/unit/custom_transport_implementation_spec.rb index 9b004e3f54..70265f25fd 100644 --- a/elasticsearch/spec/unit/custom_transport_implementation_spec.rb +++ b/elasticsearch/spec/unit/custom_transport_implementation_spec.rb @@ -19,7 +19,7 @@ describe Elasticsearch::Client do context 'when using custom transport implementation' do class MyTransport - include Elasticsearch::Transport::Transport::Base + include Elastic::Transport::Transport::Base def initialize(args); end end let(:client) { Elasticsearch::Client.new(transport_class: MyTransport) } @@ -30,9 +30,9 @@ def initialize(args); end let(:meta_header) do if jruby? - "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION}" + "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION},jv=#{ENV_JAVA['java.version']},jr=#{JRUBY_VERSION}" else - "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elasticsearch::Transport::VERSION}" + "es=#{meta_version},rb=#{RUBY_VERSION},t=#{Elastic::Transport::VERSION}" end end From 56976fff360c33d429780a2bb1b6ea3630e17888 Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Thu, 16 Sep 2021 10:59:31 +0100 Subject: [PATCH 03/11] [API] Updates elasticsearch-api for elastic-transport --- elasticsearch-api/Gemfile | 4 ---- elasticsearch-api/api-spec-testing/rspec_matchers.rb | 6 +++--- elasticsearch-api/api-spec-testing/test_file.rb | 4 ++-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/elasticsearch-api/Gemfile b/elasticsearch-api/Gemfile index b72b044c39..74ca453845 100644 --- a/elasticsearch-api/Gemfile +++ b/elasticsearch-api/Gemfile @@ -24,10 +24,6 @@ if File.exist? File.expand_path("../../elasticsearch/elasticsearch.gemspec", __F gem 'elasticsearch', path: File.expand_path('../../elasticsearch', __FILE__), require: false end -if File.exist? File.expand_path('../../elasticsearch-transport', __FILE__) - gem 'elasticsearch-transport', path: File.expand_path('../../elasticsearch-transport', __FILE__), require: true -end - group :development do gem 'rspec' gem 'rspec_junit_formatter' diff --git a/elasticsearch-api/api-spec-testing/rspec_matchers.rb b/elasticsearch-api/api-spec-testing/rspec_matchers.rb index 37bb503a6a..2dd4973ce3 100644 --- a/elasticsearch-api/api-spec-testing/rspec_matchers.rb +++ b/elasticsearch-api/api-spec-testing/rspec_matchers.rb @@ -295,12 +295,12 @@ def compare_string_response(expected_string, response) message =~ /\[400\]/ || actual_error.is_a?(ArgumentError) when 'unauthorized' - actual_error.is_a?(Elasticsearch::Transport::Transport::Errors::Unauthorized) + actual_error.is_a?(Elastic::Transport::Transport::Errors::Unauthorized) when 'forbidden' - actual_error.is_a?(Elasticsearch::Transport::Transport::Errors::Forbidden) + actual_error.is_a?(Elastic::Transport::Transport::Errors::Forbidden) when /error parsing field/ message =~ /\[400\]/ || - actual_error.is_a?(Elasticsearch::Transport::Transport::Errors::BadRequest) + actual_error.is_a?(Elastic::Transport::Transport::Errors::BadRequest) else message =~ /#{expected_error}/ end diff --git a/elasticsearch-api/api-spec-testing/test_file.rb b/elasticsearch-api/api-spec-testing/test_file.rb index 30c881edbe..23ac0ae929 100644 --- a/elasticsearch-api/api-spec-testing/test_file.rb +++ b/elasticsearch-api/api-spec-testing/test_file.rb @@ -125,7 +125,7 @@ def setup begin action.execute(client) true - rescue Elasticsearch::Transport::Transport::Errors::ServiceUnavailable => e + rescue Elastic::Transport::Transport::Errors::ServiceUnavailable => e # The action sometimes gets the cluster in a recovering state, so we # retry a few times and then raise an exception if it's still # happening @@ -403,7 +403,7 @@ def clear_snapshots_and_repositories(client) begin response = client.perform_request('DELETE', "_snapshot/#{repository}", ignore: [500, 404]) client.snapshot.delete_repository(repository: repository, ignore: 404) - rescue Elasticsearch::Transport::Transport::Errors::InternalServerError => e + rescue Elastic::Transport::Transport::Errors::InternalServerError => e regexp = /indices that use the repository: \[docs\/([a-zA-Z0-9]+)/ raise e unless response.body['error']['root_cause'].first['reason'].match(regexp) From 38f6af3a80b0a0e1807a9755b06c293085d45fdd Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Thu, 16 Sep 2021 11:08:07 +0100 Subject: [PATCH 04/11] Updates Elasticsearch rake task for elastic-transport --- rake_tasks/elasticsearch_tasks.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rake_tasks/elasticsearch_tasks.rake b/rake_tasks/elasticsearch_tasks.rake index 0ebf16a3a9..6cfd499b9d 100644 --- a/rake_tasks/elasticsearch_tasks.rake +++ b/rake_tasks/elasticsearch_tasks.rake @@ -37,7 +37,7 @@ namespace :elasticsearch do ready = true break end - rescue Elasticsearch::Transport::Transport::Errors::RequestTimeout => e + rescue Elastic::Transport::Transport::Errors::RequestTimeout => e puts "Couldn't confirm green status.\n#{e.inspect}." rescue Faraday::ConnectionFailed => e puts "Couldn't connect to Elasticsearch.\n#{e.inspect}." From 9cadddb4a1c54c3cb992f2bd958bba04e68e7cdb Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Thu, 16 Sep 2021 11:12:37 +0100 Subject: [PATCH 05/11] [CI] Removes elasticsearch-transport from GitHub Actions --- .github/workflows/main.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9d117aeccd..141d3bdda6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -40,7 +40,5 @@ jobs: run: cd elasticsearch && bundle exec rake test:all - name: elasticsearch-api run: cd elasticsearch-api && bundle exec rake test:spec test:platinum:unit - - name: elasticsearch-transport - run: cd elasticsearch-transport && bundle exec rake test:all - name: elasticsearch-dsl run: cd elasticsearch-dsl && bundle exec rake test:all From 7a25c49f429fa7071e712d8b674a3b8a784209d3 Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Thu, 16 Sep 2021 11:12:52 +0100 Subject: [PATCH 06/11] [CI] Removes elasticsearch-transport from release and test tasks --- Rakefile | 2 -- rake_tasks/test_tasks.rake | 1 - rake_tasks/unified_release_tasks.rake | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Rakefile b/Rakefile index c147aa339e..fa8956b8ea 100644 --- a/Rakefile +++ b/Rakefile @@ -58,14 +58,12 @@ require 'pathname' CURRENT_PATH = Pathname(File.expand_path(__dir__)) SUBPROJECTS = [ 'elasticsearch', - 'elasticsearch-transport', 'elasticsearch-dsl', 'elasticsearch-api' ].freeze RELEASE_TOGETHER = [ 'elasticsearch', - 'elasticsearch-transport', 'elasticsearch-api' ].freeze diff --git a/rake_tasks/test_tasks.rake b/rake_tasks/test_tasks.rake index 5d0fe8d08b..62be131917 100644 --- a/rake_tasks/test_tasks.rake +++ b/rake_tasks/test_tasks.rake @@ -17,7 +17,6 @@ UNIT_TESTED_PROJECTS = [ 'elasticsearch', - 'elasticsearch-transport', 'elasticsearch-dsl', 'elasticsearch-api' ].freeze diff --git a/rake_tasks/unified_release_tasks.rake b/rake_tasks/unified_release_tasks.rake index 86fb1b4358..827d9fd07c 100644 --- a/rake_tasks/unified_release_tasks.rake +++ b/rake_tasks/unified_release_tasks.rake @@ -72,7 +72,7 @@ namespace :unified_release do end version_regexp = Regexp.new(/VERSION = ("|'([0-9.]+(-SNAPSHOT)?)'|")/) - gemspec_regexp = Regexp.new(/('elasticsearch-transport'|'elasticsearch-api'),\s+'([0-9.]+)'/) + gemspec_regexp = Regexp.new(/('elasticsearch-api'),\s+'([0-9.]+)'/) files.flatten.each do |file| content = File.read(file) From fc7d4d4fb87b2929f03caa24f73be3cb903604e9 Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Mon, 4 Oct 2021 16:22:49 +0100 Subject: [PATCH 07/11] [Gem] Updates console task --- elasticsearch/bin/elastic_ruby_console | 2 -- 1 file changed, 2 deletions(-) diff --git a/elasticsearch/bin/elastic_ruby_console b/elasticsearch/bin/elastic_ruby_console index e532802f14..730b738ee3 100755 --- a/elasticsearch/bin/elastic_ruby_console +++ b/elasticsearch/bin/elastic_ruby_console @@ -1,12 +1,10 @@ #!/usr/bin/env ruby $LOAD_PATH.unshift(File.expand_path('../../elasticsearch/lib', __dir__)) -$LOAD_PATH.unshift(File.expand_path('../../elasticsearch-transport/lib', __dir__)) $LOAD_PATH.unshift(File.expand_path('../../elasticsearch-dsl/lib', __dir__)) $LOAD_PATH.unshift(File.expand_path('../../elasticsearch-api/lib', __dir__)) require 'elasticsearch' -require 'elasticsearch-transport' require 'elasticsearch-api' require 'elasticsearch-dsl' From 1737b49226ad01d175b16e27e1e1d70936735d46 Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Mon, 4 Oct 2021 16:25:31 +0100 Subject: [PATCH 08/11] fixup_console --- Rakefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Rakefile b/Rakefile index fa8956b8ea..7a6dff34dc 100644 --- a/Rakefile +++ b/Rakefile @@ -79,6 +79,11 @@ task :default do system 'rake --tasks' end +desc 'Run Ruby console with the Elasticsearch client libraries loaded' +task :console do + system './elasticsearch/bin/elastic_ruby_console' +end + desc 'Display information about subprojects' task :subprojects do puts '-' * 80 From 713dac9d52d56353227e91cd38eff31444efd28c Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Mon, 4 Oct 2021 16:43:41 +0100 Subject: [PATCH 09/11] [DOCS] Small fix, rename --- elasticsearch/lib/elasticsearch.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/elasticsearch/lib/elasticsearch.rb b/elasticsearch/lib/elasticsearch.rb index 66a504fdd1..ede5626cb2 100644 --- a/elasticsearch/lib/elasticsearch.rb +++ b/elasticsearch/lib/elasticsearch.rb @@ -20,7 +20,7 @@ require 'elasticsearch/api' module Elasticsearch - # This is the stateful Elasticsearch::Client, using an instance of elasticsearch-transport. + # This is the stateful Elasticsearch::Client, using an instance of elastic-transport. class Client include Elasticsearch::API # The default port to use if connecting using a Cloud ID. @@ -126,6 +126,6 @@ def self.client_meta_version Elasticsearch::VERSION end - # Constant for elasticsearch-transport meta-header + # Constant for elastic-transport meta-header ELASTICSEARCH_SERVICE_VERSION = [:es, client_meta_version].freeze end From 0718c69f8c003a7a02b4b9d9daf347c2d9673d5b Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Mon, 4 Oct 2021 17:12:58 +0100 Subject: [PATCH 10/11] Updates elasticsearch Gemfile --- elasticsearch/Gemfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/elasticsearch/Gemfile b/elasticsearch/Gemfile index e22add2629..425340324f 100644 --- a/elasticsearch/Gemfile +++ b/elasticsearch/Gemfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # Licensed to Elasticsearch B.V. under one or more contributor # license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright @@ -20,6 +22,6 @@ source 'https://rubygems.org' # Specify your gem's dependencies in elasticsearch.gemspec gemspec -if File.exist? File.expand_path('../../elasticsearch-api/elasticsearch-api.gemspec', __FILE__) - gem 'elasticsearch-api', path: File.expand_path('../../elasticsearch-api', __FILE__), require: false +if File.exist? File.expand_path('../elasticsearch-api/elasticsearch-api.gemspec', __dir__) + gem 'elasticsearch-api', path: File.expand_path('../elasticsearch-api', __dir__), require: false end From 444004544c042622d3a319cc655a7baa16874be3 Mon Sep 17 00:00:00 2001 From: Fernando Briano Date: Mon, 4 Oct 2021 17:13:10 +0100 Subject: [PATCH 11/11] [CI] Updates unified release assemble task --- rake_tasks/unified_release_tasks.rake | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/rake_tasks/unified_release_tasks.rake b/rake_tasks/unified_release_tasks.rake index 827d9fd07c..7f3b5f184d 100644 --- a/rake_tasks/unified_release_tasks.rake +++ b/rake_tasks/unified_release_tasks.rake @@ -28,7 +28,7 @@ namespace :unified_release do args[:version] end - Rake::Task['update_version'].invoke(Elasticsearch::VERSION, @version) unless @version == Elasticsearch::VERSION + Rake::Task['unified_release:bump'].invoke(@version) unless @version == Elasticsearch::VERSION build_gems(args[:output_dir]) create_zip_file(args[:output_dir]) @@ -72,22 +72,22 @@ namespace :unified_release do end version_regexp = Regexp.new(/VERSION = ("|'([0-9.]+(-SNAPSHOT)?)'|")/) - gemspec_regexp = Regexp.new(/('elasticsearch-api'),\s+'([0-9.]+)'/) + gemspec_regexp = Regexp.new(/'elasticsearch-api',\s+'([0-9.]+)'/) files.flatten.each do |file| content = File.read(file) - regexp = file.match?('gemspec') ? gemspec_regexp : version_regexp - - if (match = content.match(regexp)) - old_version = match[2] - content.gsub!(old_version, args[:version]) - puts "[#{old_version}] -> [#{args[:version]}] in #{file.gsub('./','')}" - File.open(file, 'w') { |f| f.puts content } + if file.match?('gemspec') + match = content.match(gemspec_regexp) + content.gsub!(match[0], "'elasticsearch-api', '#{args[:version]}'") else - puts "- [#{file}]".ljust(longest_line+20) + " -" + match = content.match(version_regexp) + old_version = match[1] + content.gsub!(old_version, "'#{args[:version]}'") end - rescue StandardError => e - abort "[!!!] #{e.class} : #{e.message}" + puts "[#{old_version}] -> [#{args[:version]}] in #{file.gsub('./','')}" + File.open(file, 'w') { |f| f.puts content } end + rescue StandardError => e + abort "[!!!] #{e.class} : #{e.message}" end end