elasticsearch-transport-pixlee 1.0.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +16 -0
  4. data/LICENSE.txt +13 -0
  5. data/README.md +441 -0
  6. data/Rakefile +80 -0
  7. data/elasticsearch-transport.gemspec +74 -0
  8. data/lib/elasticsearch-transport.rb +1 -0
  9. data/lib/elasticsearch/transport.rb +30 -0
  10. data/lib/elasticsearch/transport/client.rb +195 -0
  11. data/lib/elasticsearch/transport/transport/base.rb +261 -0
  12. data/lib/elasticsearch/transport/transport/connections/collection.rb +93 -0
  13. data/lib/elasticsearch/transport/transport/connections/connection.rb +121 -0
  14. data/lib/elasticsearch/transport/transport/connections/selector.rb +63 -0
  15. data/lib/elasticsearch/transport/transport/errors.rb +73 -0
  16. data/lib/elasticsearch/transport/transport/http/curb.rb +87 -0
  17. data/lib/elasticsearch/transport/transport/http/faraday.rb +60 -0
  18. data/lib/elasticsearch/transport/transport/http/manticore.rb +124 -0
  19. data/lib/elasticsearch/transport/transport/response.rb +21 -0
  20. data/lib/elasticsearch/transport/transport/serializer/multi_json.rb +36 -0
  21. data/lib/elasticsearch/transport/transport/sniffer.rb +46 -0
  22. data/lib/elasticsearch/transport/version.rb +5 -0
  23. data/test/integration/client_test.rb +144 -0
  24. data/test/integration/transport_test.rb +73 -0
  25. data/test/profile/client_benchmark_test.rb +125 -0
  26. data/test/test_helper.rb +76 -0
  27. data/test/unit/client_test.rb +274 -0
  28. data/test/unit/connection_collection_test.rb +88 -0
  29. data/test/unit/connection_selector_test.rb +64 -0
  30. data/test/unit/connection_test.rb +100 -0
  31. data/test/unit/response_test.rb +15 -0
  32. data/test/unit/serializer_test.rb +16 -0
  33. data/test/unit/sniffer_test.rb +145 -0
  34. data/test/unit/transport_base_test.rb +478 -0
  35. data/test/unit/transport_curb_test.rb +97 -0
  36. data/test/unit/transport_faraday_test.rb +140 -0
  37. data/test/unit/transport_manticore_test.rb +118 -0
  38. metadata +408 -0
@@ -0,0 +1,124 @@
1
+ require 'manticore'
2
+
3
+ module Elasticsearch
4
+ module Transport
5
+ module Transport
6
+ module HTTP
7
+ # Alternative HTTP transport implementation for JRuby,
8
+ # using the [_Manticore_](https://github.com/cheald/manticore) client,
9
+ #
10
+ # @example HTTP
11
+ #
12
+ # require 'elasticsearch/transport/transport/http/manticore'
13
+ #
14
+ # client = Elasticsearch::Client.new transport_class: Elasticsearch::Transport::Transport::HTTP::Manticore
15
+ #
16
+ # client.transport.connections.first.connection
17
+ # => #<Manticore::Client:0x56bf7ca6 ...>
18
+ #
19
+ # client.info['status']
20
+ # => 200
21
+ #
22
+ # @example HTTPS (All SSL settings are optional,
23
+ # see http://www.rubydoc.info/gems/manticore/Manticore/Client:initialize)
24
+ #
25
+ # require 'elasticsearch/transport/transport/http/manticore'
26
+ #
27
+ # client = Elasticsearch::Client.new \
28
+ # url: 'https://elasticsearch.example.com',
29
+ # transport_class: Elasticsearch::Transport::Transport::HTTP::Manticore,
30
+ # ssl: {
31
+ # truststore: '/tmp/truststore.jks',
32
+ # truststore_password: 'password',
33
+ # keystore: '/tmp/keystore.jks',
34
+ # keystore_password: 'secret',
35
+ # }
36
+ #
37
+ # client.transport.connections.first.connection
38
+ # => #<Manticore::Client:0xdeadbeef ...>
39
+ #
40
+ # client.info['status']
41
+ # => 200
42
+ #
43
+ # @see Transport::Base
44
+ #
45
+ class Manticore
46
+ include Base
47
+
48
+ # Performs the request by invoking {Transport::Base#perform_request} with a block.
49
+ #
50
+ # @return [Response]
51
+ # @see Transport::Base#perform_request
52
+ #
53
+ def perform_request(method, path, params={}, body=nil)
54
+ super do |connection, url|
55
+ params[:body] = __convert_to_json(body) if body
56
+ params = params.merge @request_options
57
+ case method
58
+ when "GET"
59
+ resp = connection.connection.get(url, params)
60
+ when "HEAD"
61
+ resp = connection.connection.head(url, params)
62
+ when "PUT"
63
+ resp = connection.connection.put(url, params)
64
+ when "POST"
65
+ resp = connection.connection.post(url, params)
66
+ when "DELETE"
67
+ resp = connection.connection.delete(url, params)
68
+ else
69
+ raise ArgumentError.new "Method #{method} not supported"
70
+ end
71
+ Response.new resp.code, resp.read_body, resp.headers
72
+ end
73
+ end
74
+
75
+ # Builds and returns a collection of connections.
76
+ # Each connection is a Manticore::Client
77
+ #
78
+ # @return [Connections::Collection]
79
+ #
80
+ def __build_connections
81
+ @request_options = {}
82
+
83
+ if options.key?(:headers)
84
+ @request_options[:headers] = options[:headers]
85
+ end
86
+
87
+ client_options = options[:transport_options] || {}
88
+ client_options[:ssl] = options[:ssl] || {}
89
+
90
+ Connections::Collection.new \
91
+ :connections => hosts.map { |host|
92
+ host[:protocol] = host[:scheme] || DEFAULT_PROTOCOL
93
+ host[:port] ||= DEFAULT_PORT
94
+
95
+ host.delete(:user) # auth is not supported here.
96
+ host.delete(:password) # use the headers
97
+
98
+ url = __full_url(host)
99
+
100
+ Connections::Connection.new \
101
+ :host => host,
102
+ :connection => ::Manticore::Client.new(client_options)
103
+ },
104
+ :selector_class => options[:selector_class],
105
+ :selector => options[:selector]
106
+ end
107
+
108
+ # Returns an array of implementation specific connection errors.
109
+ #
110
+ # @return [Array]
111
+ #
112
+ def host_unreachable_exceptions
113
+ [
114
+ ::Manticore::Timeout,
115
+ ::Manticore::SocketException,
116
+ ::Manticore::ClientProtocolException,
117
+ ::Manticore::ResolutionFailure
118
+ ]
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,21 @@
1
+ module Elasticsearch
2
+ module Transport
3
+ module Transport
4
+
5
+ # Wraps the response from Elasticsearch.
6
+ #
7
+ class Response
8
+ attr_reader :status, :body, :headers
9
+
10
+ # @param status [Integer] Response status code
11
+ # @param body [String] Response body
12
+ # @param headers [Hash] Response headers
13
+ def initialize(status, body, headers={})
14
+ @status, @body, @headers = status, body, headers
15
+ @body = body.force_encoding('UTF-8') if body.respond_to?(:force_encoding)
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,36 @@
1
+ module Elasticsearch
2
+ module Transport
3
+ module Transport
4
+ module Serializer
5
+
6
+ # An abstract class for implementing serializer implementations
7
+ #
8
+ module Base
9
+ # @param transport [Object] The instance of transport which uses this serializer
10
+ #
11
+ def initialize(transport=nil)
12
+ @transport = transport
13
+ end
14
+ end
15
+
16
+ # A default JSON serializer (using [MultiJSON](http://rubygems.org/gems/multi_json))
17
+ #
18
+ class MultiJson
19
+ include Base
20
+
21
+ # De-serialize a Hash from JSON string
22
+ #
23
+ def load(string, options={})
24
+ ::MultiJson.load(string, options)
25
+ end
26
+
27
+ # Serialize a Hash to JSON string
28
+ #
29
+ def dump(object, options={})
30
+ ::MultiJson.dump(object, options)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,46 @@
1
+ module Elasticsearch
2
+ module Transport
3
+ module Transport
4
+
5
+ # Handles node discovery ("sniffing").
6
+ #
7
+ class Sniffer
8
+ RE_URL = /\/([^:]*):([0-9]+)\]/ # Use named groups on Ruby 1.9: /\/(?<host>[^:]*):(?<port>[0-9]+)\]/
9
+
10
+ attr_reader :transport
11
+ attr_accessor :timeout
12
+
13
+ # @param transport [Object] A transport instance.
14
+ #
15
+ def initialize(transport)
16
+ @transport = transport
17
+ @timeout = transport.options[:sniffer_timeout] || 1
18
+ end
19
+
20
+ # Retrieves the node list from the Elasticsearch's
21
+ # [_Nodes Info API_](http://www.elasticsearch.org/guide/reference/api/admin-cluster-nodes-info/)
22
+ # and returns a normalized Array of information suitable for passing to transport.
23
+ #
24
+ # Shuffles the collection before returning it when the `randomize_hosts` option is set for transport.
25
+ #
26
+ # @return [Array<Hash>]
27
+ # @raise [SnifferTimeoutError]
28
+ #
29
+ def hosts
30
+ Timeout::timeout(timeout, SnifferTimeoutError) do
31
+ nodes = transport.perform_request('GET', '_nodes/http').body
32
+ hosts = nodes['nodes'].map do |id,info|
33
+ if matches = info["#{transport.protocol}_address"].to_s.match(RE_URL)
34
+ # TODO: Implement lightweight "indifferent access" here
35
+ info.merge :host => matches[1], :port => matches[2], :id => id
36
+ end
37
+ end.compact
38
+
39
+ hosts.shuffle! if transport.options[:randomize_hosts]
40
+ hosts
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,5 @@
1
+ module Elasticsearch
2
+ module Transport
3
+ VERSION = "1.0.13"
4
+ end
5
+ end
@@ -0,0 +1,144 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Transport::ClientIntegrationTest < Elasticsearch::Test::IntegrationTestCase
4
+ startup do
5
+ Elasticsearch::Extensions::Test::Cluster.start(nodes: 2) if ENV['SERVER'] and not Elasticsearch::Extensions::Test::Cluster.running?
6
+ end
7
+
8
+ context "Elasticsearch client" do
9
+ teardown do
10
+ begin; Object.send(:remove_const, :Typhoeus); rescue NameError; end
11
+ begin; Object.send(:remove_const, :Patron); rescue NameError; end
12
+ end
13
+
14
+ setup do
15
+ @port = (ENV['TEST_CLUSTER_PORT'] || 9250).to_i
16
+ system "curl -X DELETE http://localhost:#{@port}/_all > /dev/null 2>&1"
17
+
18
+ @logger = Logger.new(STDERR)
19
+ @logger.formatter = proc do |severity, datetime, progname, msg|
20
+ color = case severity
21
+ when /INFO/ then :green
22
+ when /ERROR|WARN|FATAL/ then :red
23
+ when /DEBUG/ then :cyan
24
+ else :white
25
+ end
26
+ ANSI.ansi(severity[0] + ' ', color, :faint) + ANSI.ansi(msg, :white, :faint) + "\n"
27
+ end
28
+
29
+ @client = Elasticsearch::Client.new host: "localhost:#{@port}"
30
+ end
31
+
32
+ should "connect to the cluster" do
33
+ assert_nothing_raised do
34
+ response = @client.perform_request 'GET', '_cluster/health'
35
+ assert_equal 2, response.body['number_of_nodes']
36
+ end
37
+ end
38
+
39
+ should "handle paths and URL parameters" do
40
+ @client.perform_request 'PUT', 'myindex/mydoc/1', {routing: 'XYZ'}, {foo: 'bar'}
41
+ @client.perform_request 'GET', '_cluster/health?wait_for_status=green', {}
42
+
43
+ response = @client.perform_request 'GET', 'myindex/mydoc/1?routing=XYZ'
44
+ assert_equal 200, response.status
45
+ assert_equal 'bar', response.body['_source']['foo']
46
+
47
+ assert_raise Elasticsearch::Transport::Transport::Errors::NotFound do
48
+ @client.perform_request 'GET', 'myindex/mydoc/1?routing=ABC'
49
+ end
50
+ end
51
+
52
+ should "pass options to the transport" do
53
+ @client = Elasticsearch::Client.new \
54
+ host: "localhost:#{@port}",
55
+ logger: (ENV['QUIET'] ? nil : @logger),
56
+ transport_options: { headers: { content_type: 'application/yaml' } }
57
+
58
+ response = @client.perform_request 'GET', '_cluster/health'
59
+ assert_match /---\ncluster_name:/, response.body.to_s
60
+ end
61
+
62
+ context "with round robin selector" do
63
+ setup do
64
+ @client = Elasticsearch::Client.new \
65
+ hosts: ["localhost:#{@port}", "localhost:#{@port+1}" ],
66
+ logger: (ENV['QUIET'] ? nil : @logger)
67
+ end
68
+
69
+ should "rotate nodes" do
70
+ # Hit node 1
71
+ response = @client.perform_request 'GET', '_nodes/_local'
72
+ assert_equal 'node-1', response.body['nodes'].to_a[0][1]['name']
73
+
74
+ # Hit node 2
75
+ response = @client.perform_request 'GET', '_nodes/_local'
76
+ assert_equal 'node-2', response.body['nodes'].to_a[0][1]['name']
77
+
78
+ # Hit node 1
79
+ response = @client.perform_request 'GET', '_nodes/_local'
80
+ assert_equal 'node-1', response.body['nodes'].to_a[0][1]['name']
81
+ end
82
+ end
83
+
84
+ context "with a sick node and retry on failure" do
85
+ setup do
86
+ @port = (ENV['TEST_CLUSTER_PORT'] || 9250).to_i
87
+ @client = Elasticsearch::Client.new \
88
+ hosts: ["localhost:#{@port}", "foobar1"],
89
+ logger: (ENV['QUIET'] ? nil : @logger),
90
+ retry_on_failure: true
91
+ end
92
+
93
+ should "retry the request with next server" do
94
+ assert_nothing_raised do
95
+ 5.times { @client.perform_request 'GET', '_nodes/_local' }
96
+ end
97
+ end
98
+
99
+ should "raise exception when it cannot get any healthy server" do
100
+ @client = Elasticsearch::Client.new \
101
+ hosts: ["localhost:#{@port}", "foobar1", "foobar2", "foobar3"],
102
+ logger: (ENV['QUIET'] ? nil : @logger),
103
+ retry_on_failure: 1
104
+
105
+ assert_nothing_raised do
106
+ # First hit is OK
107
+ @client.perform_request 'GET', '_nodes/_local'
108
+ end
109
+
110
+ assert_raise Faraday::Error::ConnectionFailed do
111
+ # Second hit fails
112
+ @client.perform_request 'GET', '_nodes/_local'
113
+ end
114
+ end
115
+ end
116
+
117
+ context "with a sick node and reloading on failure" do
118
+ setup do
119
+ @client = Elasticsearch::Client.new \
120
+ hosts: ["localhost:#{@port}", "foobar1", "foobar2"],
121
+ logger: (ENV['QUIET'] ? nil : @logger),
122
+ reload_on_failure: true
123
+ end
124
+
125
+ should "reload the connections" do
126
+ assert_equal 3, @client.transport.connections.size
127
+ assert_nothing_raised do
128
+ 5.times { @client.perform_request 'GET', '_nodes/_local' }
129
+ end
130
+ assert_equal 2, @client.transport.connections.size
131
+ end
132
+ end
133
+
134
+ context "with Faraday adapters" do
135
+ should "automatically use the Patron client when loaded" do
136
+ require 'patron'
137
+ client = Elasticsearch::Transport::Client.new host: "localhost:#{@port}"
138
+
139
+ response = @client.perform_request 'GET', '_cluster/health'
140
+ assert_equal 200, response.status
141
+ end unless JRUBY
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,73 @@
1
+ require 'test_helper'
2
+
3
+ class Elasticsearch::Transport::ClientIntegrationTest < Elasticsearch::Test::IntegrationTestCase
4
+ startup do
5
+ Elasticsearch::Extensions::Test::Cluster.start(nodes: 2) if ENV['SERVER'] and not Elasticsearch::Extensions::Test::Cluster.running?
6
+ end
7
+
8
+ context "Transport" do
9
+ setup do
10
+ @port = (ENV['TEST_CLUSTER_PORT'] || 9250).to_i
11
+ begin; Object.send(:remove_const, :Patron); rescue NameError; end
12
+ end
13
+
14
+ should "allow to customize the Faraday adapter" do
15
+ require 'typhoeus'
16
+ require 'typhoeus/adapters/faraday'
17
+
18
+ transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new \
19
+ :hosts => [ { :host => 'localhost', :port => @port } ] do |f|
20
+ f.response :logger
21
+ f.adapter :typhoeus
22
+ end
23
+
24
+ client = Elasticsearch::Transport::Client.new transport: transport
25
+ client.perform_request 'GET', ''
26
+ end
27
+
28
+ should "allow to define connection parameters and pass them" do
29
+ transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new \
30
+ :hosts => [ { :host => 'localhost', :port => @port } ],
31
+ :options => { :transport_options => {
32
+ :params => { :format => 'yaml' }
33
+ }
34
+ }
35
+
36
+ client = Elasticsearch::Transport::Client.new transport: transport
37
+ response = client.perform_request 'GET', ''
38
+
39
+ assert response.body.start_with?("---\n"), "Response body should be YAML: #{response.body.inspect}"
40
+ end
41
+
42
+ should "use the Curb client" do
43
+ require 'curb'
44
+ require 'elasticsearch/transport/transport/http/curb'
45
+
46
+ transport = Elasticsearch::Transport::Transport::HTTP::Curb.new \
47
+ :hosts => [ { :host => 'localhost', :port => @port } ] do |curl|
48
+ curl.verbose = true
49
+ end
50
+
51
+ client = Elasticsearch::Transport::Client.new transport: transport
52
+ client.perform_request 'GET', ''
53
+ end unless JRUBY
54
+
55
+ should "deserialize JSON responses in the Curb client" do
56
+ require 'curb'
57
+ require 'elasticsearch/transport/transport/http/curb'
58
+
59
+ transport = Elasticsearch::Transport::Transport::HTTP::Curb.new \
60
+ :hosts => [ { :host => 'localhost', :port => @port } ] do |curl|
61
+ curl.verbose = true
62
+ end
63
+
64
+ client = Elasticsearch::Transport::Client.new transport: transport
65
+ response = client.perform_request 'GET', ''
66
+
67
+ assert_respond_to(response.body, :to_hash)
68
+ assert_not_nil response.body['name']
69
+ assert_equal 'application/json', response.headers['content-type']
70
+ end unless JRUBY
71
+ end
72
+
73
+ end