dse-driver 1.0.1-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 43d6f472d065e4e4d980d6402fe26da4ab2f8259
4
+ data.tar.gz: 182fd750d7dc29158fe46a1e1d7063c2a41622bc
5
+ SHA512:
6
+ metadata.gz: 986c12a9a51f42ee91253bf7fe25120e04b8877fd92f4e52332892f819d21a5e9aef639b208f8cb7d53f35db3cff822c14c1291d1f3917f95a4f46dcaccda270
7
+ data.tar.gz: 4515f1d9fd062288aea8de3f755467e1c749e1a5e2284862293703c290c9246f2b897e64208971176f4cd2d51c43593e2bec330df1365ae561a0e49879d772be
data/.yardopts ADDED
@@ -0,0 +1,13 @@
1
+ --no-private
2
+ --markup markdown
3
+ --type-tag expected_errors:"Expected Errors"
4
+ --tag jira_ticket:"JIRA Ticket"
5
+ --type-tag expected_result:"Expected Result"
6
+ --tag test_assumptions:"Test Assumptions"
7
+ --tag test_category:"Test Category"
8
+
9
+ lib/**/*.rb
10
+ integration/**/*.rb
11
+ -- README
12
+
13
+ load_plugins: true
data/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # DataStax Enterprise Ruby Driver
2
+
3
+ *NOTE: The DataStax Enterprise Ruby Driver can be used solely with DataStax Enterprise. Please consult [the license](http://www.datastax.com/terms/datastax-dse-driver-license-terms).*
4
+
5
+ This driver is built on top of the [DataStax Ruby driver for Apache Cassandra](http://docs.datastax.com/en/latest-ruby-driver/ruby-driver/whatsNew.html)
6
+ and enhanced for the adaptive data management and mixed workload capabilities
7
+ provided by DSE. Therefore a lot of the underlying concepts are the same.
8
+
9
+ ## Documentation
10
+ Driver documentation can be found [here](http://docs.datastax.com/en/latest-dse-ruby-driver/ruby-driver/whatsNew.html).
11
+
12
+ In particular, you'll find our [Features](http://docs.datastax.com/en/latest-dse-ruby-driver/supplemental/features) and
13
+ [API](http://docs.datastax.com/en/latest-dse-ruby-driver/supplemental/api) sections very enlightening.
14
+
15
+ ## Feedback Requested
16
+ *Help us focus our efforts!* [Provide your input](http://goo.gl/forms/pCs8PTpHLf) on the Ruby Driver
17
+ Platform and Runtime Survey (we kept it short).
18
+
19
+ If you find an issue, please file an issue in our [public JIRA](https://datastax-oss.atlassian.net/browse/RUBY).
20
+ *Please be sure to specify the affects-version (DSE-1.X.Y).*
21
+
22
+ You can also post questions in [our forum](https://groups.google.com/a/lists.datastax.com/forum/#!forum/ruby-driver-user).
23
+
24
+ ## Features
25
+
26
+ This driver exposes the following features of DSE 5.0:
27
+
28
+ * <a href="features/graph#graph">Graph</a>
29
+ * <a href="features/authentication#authentication">Authentication</a> with nodes running DSE
30
+ * <a href="features/geospatial#geospatial-types">Geospatial types</a>
31
+
32
+ Note that this driver is fully compatible with previous versions of DataStax Enterprise.
33
+
34
+ ## Installation
35
+ The driver is named dse-driver on rubygems.org and can easily be installed with Bundler or the gem program. It will
36
+ download the appropriate Cassandra driver as well.
37
+
38
+ ## Upgrade
39
+ The driver is intended to have the same look and feel as the core driver to make upgrading from the core driver
40
+ trivial. The only change is to replace references to the <code>Cassandra</code> module with <code>Dse</code> when
41
+ creating the cluster object:
42
+
43
+ ```ruby
44
+ require 'dse'
45
+
46
+ # This returns a Dse::Cluster instance
47
+ cluster = Dse.cluster
48
+
49
+ # This returns a Dse::Session instance
50
+ session = cluster.connect
51
+ rs = session.execute('select * from system.local')
52
+ ```
53
+
54
+ ## Determining driver versions
55
+ Within a script or irb, you can determine the exact versions of the dse and core drivers by accessing the VERSION
56
+ constant of the appropriate module:
57
+
58
+ ```ruby
59
+ require 'dse'
60
+
61
+ puts "Dse Driver Version: #{Dse::VERSION}"
62
+ puts "Cassandra Driver Version: #{Cassandra::VERSION}"
63
+ ```
64
+
65
+ ## Compatibility
66
+ Although this driver exposes new features introduced in DSE 5.0, it is fully compatible and supported for use with
67
+ previous versions of DSE.
68
+
69
+ ## License
70
+ Copyright (C) 2016 DataStax Inc.
71
+
72
+ The full license terms are available at http://www.datastax.com/terms/datastax-dse-driver-license-terms
Binary file
data/lib/dse.rb ADDED
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright (C) 2016 DataStax Inc.
5
+ #
6
+ # This software can be used solely with DataStax Enterprise. Please consult the license at
7
+ # http://www.datastax.com/terms/datastax-dse-driver-license-terms
8
+ #++
9
+
10
+ require 'json'
11
+
12
+ if RUBY_ENGINE == 'jruby'
13
+ require 'challenge_evaluator'
14
+ else
15
+ require 'gss_api_context'
16
+ end
17
+
18
+ require 'cassandra'
19
+
20
+ module Dse
21
+ # Creates a {Dse::Cluster Cluster instance}, which extends {http://dsdocs30/api/cassandra/cluster Cassandra::Cluster}.
22
+ # The API is identical, except that it returns a {Dse::Session Dse::Session} (see below). It takes all of the same
23
+ # options as Cassandra.cluster and the following extra options.
24
+ #
25
+ # @option options [Dse::Graph::Options] :graph_options options for the DSE graph statement handler. Takes
26
+ # priority over other `:graph_*` options specified below.
27
+ # @option options [String] :graph_name name of graph to use in graph statements
28
+ # @option options [String] :graph_source graph traversal source
29
+ # @option options [String] :graph_language language used in graph queries
30
+ # @option options [Cassandra::CONSISTENCIES] :graph_read_consistency read consistency level for graph statements.
31
+ # Overrides the standard statement consistency level
32
+ # @option options [Cassandra::CONSISTENCIES] :graph_write_consistency write consistency level for graph statements.
33
+ # Overrides the standard statement consistency level
34
+ #
35
+ # @example Connecting to localhost
36
+ # cluster = Dse.cluster
37
+ #
38
+ # @example Configuring {Dse::Cluster}
39
+ # cluster = Dse.cluster(
40
+ # username: username,
41
+ # password: password,
42
+ # hosts: ['10.0.1.1', '10.0.1.2', '10.0.1.3']
43
+ # )
44
+ #
45
+ # @return [Dse::Cluster] a cluster instance
46
+ def self.cluster(options = {})
47
+ cluster_async(options).get
48
+ end
49
+
50
+ # Creates a {Dse::Cluster Cluster instance}.
51
+ #
52
+ # @see Dse.cluster
53
+ #
54
+ # @return [Cassandra::Future<Dse::Cluster>] a future resolving to the
55
+ # cluster instance.
56
+ def self.cluster_async(options = {})
57
+ graph_options = if !options[:graph_options].nil?
58
+ Cassandra::Util.assert_instance_of(Dse::Graph::Options, options[:graph_options])
59
+ options[:graph_options]
60
+ else
61
+ Dse::Graph::Options.new(options)
62
+ end
63
+ username = options[:username]
64
+ password = options[:password]
65
+ options[:custom_types] ||= []
66
+ options[:custom_types] << Dse::Geometry::Point << Dse::Geometry::LineString << Dse::Geometry::Polygon
67
+ options, hosts = Cassandra.validate_and_massage_options(options)
68
+
69
+ # Use the DSE plain text authenticator if we have a username and password. The above validation already
70
+ # raises an error if one is given without the other.
71
+ options[:auth_provider] = Auth::Providers::Password.new(username, password) if username && password
72
+ rescue => e
73
+ futures = options.fetch(:futures_factory) { return Cassandra::Future::Error.new(e) }
74
+ futures.error(e)
75
+ else
76
+ options[:cluster_klass] = Dse::Cluster
77
+ driver = ::Cassandra::Driver.new(options)
78
+
79
+ # Wrap the load-balancing policy that we'd otherwise run with, with a host-targeting policy.
80
+ # We do this before driver.connect because driver.connect saves off the policy in the cluster
81
+ # registry and does a few other things.
82
+
83
+ lbp = driver.load_balancing_policy
84
+ driver.load_balancing_policy = Dse::LoadBalancing::Policies::HostTargeting.new(lbp)
85
+ future = driver.connect(hosts)
86
+ future.then do |cluster|
87
+ cluster.graph_options.merge!(graph_options)
88
+ cluster
89
+ end
90
+ end
91
+ end
92
+
93
+ require 'dse/cluster'
94
+ require 'dse/util/endian_buffer'
95
+ require 'dse/geometry/line_string'
96
+ require 'dse/geometry/point'
97
+ require 'dse/geometry/polygon'
98
+ require 'dse/session'
99
+ require 'dse/version'
100
+ require 'dse/graph'
101
+ require 'dse/load_balancing/policies/host_targeting'
102
+ require 'dse/statements'
103
+ require 'dse/auth/providers/gss_api'
104
+ require 'dse/auth/providers/password'
@@ -0,0 +1,160 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright (C) 2016 DataStax Inc.
5
+ #
6
+ # This software can be used solely with DataStax Enterprise. Please consult the license at
7
+ # http://www.datastax.com/terms/datastax-dse-driver-license-terms
8
+ #++
9
+
10
+ module Dse
11
+ module Auth
12
+ module Providers
13
+ # Auth provider to authenticate with Kerberos. Whenever the client connects to a DSE node,
14
+ # this provider will perform Kerberos authentication operations with it. By default, the provider
15
+ # takes the ip address of the node and uses `Socket#getnameinfo` to find its name in order to construct
16
+ # the full service address (e.g. service@host).
17
+ #
18
+ # @see #initialize
19
+ class GssApi < Cassandra::Auth::Provider
20
+ # @private
21
+ class NameInfoResolver
22
+ def resolve(host)
23
+ Socket.getnameinfo(['AF_INET', 0, host])[0]
24
+ end
25
+ end
26
+
27
+ # @private
28
+ class NoOpResolver
29
+ def resolve(host)
30
+ host
31
+ end
32
+ end
33
+
34
+ # @private
35
+ class Authenticator
36
+ # Copied from kerberosgss.h
37
+ AUTH_GSS_COMPLETE = 1
38
+
39
+ def initialize(authentication_class, host, service, principal, ticket_cache)
40
+ @authentication_class = authentication_class
41
+ @host = host
42
+ @service = service
43
+ @principal = principal
44
+ @ticket_cache = ticket_cache
45
+
46
+ if RUBY_ENGINE == 'jruby'
47
+ @sasl_client = javax.security.sasl.Sasl.createSaslClient(['GSSAPI'],
48
+ nil,
49
+ service,
50
+ host,
51
+ {javax.security.sasl.Sasl::SERVER_AUTH => 'true',
52
+ javax.security.sasl.Sasl::QOP => 'auth'},
53
+ nil)
54
+ config = Dse::Auth::Providers::ChallengeEvaluator.make_configuration(principal, ticket_cache)
55
+ login = javax.security.auth.login.LoginContext.new('DseClient', nil, nil, config)
56
+ login.login
57
+ @subject = login.getSubject
58
+ else
59
+ @gss_context = GssApiContext.new("#{@service}@#{@host}", @principal, @ticket_cache)
60
+ end
61
+ rescue => e
62
+ raise Cassandra::Errors::AuthenticationError.new(
63
+ "Failed to authenticate: #{e.message}",
64
+ nil,
65
+ nil,
66
+ nil,
67
+ nil,
68
+ nil,
69
+ nil,
70
+ :one,
71
+ 0
72
+ )
73
+ end
74
+
75
+ def initial_response
76
+ @authentication_class == 'com.datastax.bdp.cassandra.auth.DseAuthenticator' ?
77
+ 'GSSAPI' :
78
+ challenge_response('GSSAPI-START')
79
+ end
80
+
81
+ if RUBY_ENGINE == 'jruby'
82
+ def challenge_response(token)
83
+ if token == 'GSSAPI-START'
84
+ return '' unless @sasl_client.hasInitialResponse
85
+ token = ''
86
+ end
87
+
88
+ Dse::Auth::Providers::ChallengeEvaluator.evaluate(@sasl_client, @subject, token)
89
+ end
90
+ else
91
+ def challenge_response(token)
92
+ if token == 'GSSAPI-START'
93
+ response = @gss_context.step('')[1]
94
+ elsif !@is_gss_complete
95
+ # Process the challenge as a next step in authentication until we have gotten
96
+ # AUTH_GSS_COMPLETE.
97
+ rc, response = @gss_context.step(token)
98
+ @is_gss_complete = true if rc == AUTH_GSS_COMPLETE
99
+ response ||= ''
100
+ else
101
+ # Ok, we went through all initial phases of auth and now the server is giving us a message
102
+ # to decode.
103
+ data = @gss_context.unwrap(token)
104
+
105
+ raise 'Bad response from server' if data.length != 4
106
+ parsed = data.unpack('>L').first
107
+ max_length = [parsed & 0xffffff, 65536].min
108
+
109
+ # Set up a response like this:
110
+ # byte 0: the selected qop. 1==auth
111
+ # byte 1-3: the max length for any buffer sent back and forth on this connection. (big endian)
112
+ # the rest of the buffer: the authorization user name in UTF-8 - not null terminated.
113
+
114
+ user_name = @gss_context.user_name
115
+ out = [max_length | 1 << 24].pack('>L') + user_name
116
+ response = @gss_context.wrap(out)
117
+ end
118
+ response
119
+ end
120
+ end
121
+
122
+ def authentication_successful(token)
123
+ end
124
+ end
125
+
126
+ # @param service [String] name of the kerberos service; defaults to 'dse'.
127
+ # @param host_resolver [Boolean, Object] whether to use a host-resolver. By default,
128
+ # `Socket#getnameinfo` is used. To disable host-resolution, specify a `false` value. You may also
129
+ # provide a custom resolver, which is an object that implements the `resolve(host_ip)` method.
130
+ # @param principal [String] The principal whose cached credentials are used to authenticate. Defaults
131
+ # to the first principal stored in the ticket cache.
132
+ # @param ticket_cache [String] The ticket cache containing the cached credential we seek. Defaults
133
+ # *on Linux* to /tmp/krb5cc_&lt;uid&gt; (where uid is the numeric uid of the user running the
134
+ # client program). In MRI only, the `KRB5CCNAME` environment variable supercedes this. On Mac,
135
+ # the default is a symbolic reference to a ticket-cache server process.
136
+ def initialize(service = 'dse', host_resolver = true, principal = nil, ticket_cache = nil)
137
+ @service = service
138
+ @host_resolver = case host_resolver
139
+ when false
140
+ NoOpResolver.new
141
+ when true
142
+ NameInfoResolver.new
143
+ else
144
+ host_resolver
145
+ end
146
+ Cassandra::Util.assert_responds_to(:resolve, @host_resolver,
147
+ 'invalid host_resolver: it must have the :resolve method')
148
+ @principal = principal
149
+ @ticket_cache = ticket_cache
150
+ end
151
+
152
+ # @private
153
+ def create_authenticator(authentication_class, host)
154
+ Authenticator.new(authentication_class, @host_resolver.resolve(host.ip.to_s),
155
+ @service, @principal, @ticket_cache)
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright (C) 2016 DataStax Inc.
5
+ #
6
+ # This software can be used solely with DataStax Enterprise. Please consult the license at
7
+ # http://www.datastax.com/terms/datastax-dse-driver-license-terms
8
+ #++
9
+
10
+ module Dse
11
+ module Auth
12
+ module Providers
13
+ # Auth provider to authenticate with username/password for DSE's built-in authentication as well as LDAP.
14
+ #
15
+ # @note No need to instantiate this class manually, use `:username` and
16
+ # `:password` options when calling {Dse.cluster} and one will be
17
+ # created automatically for you.
18
+
19
+ class Password < Cassandra::Auth::Provider
20
+ # @private
21
+ class Authenticator
22
+ def initialize(authentication_class, username, password)
23
+ @authentication_class = authentication_class
24
+ @username = username
25
+ @password = password
26
+ end
27
+
28
+ def initial_response
29
+ @authentication_class == 'com.datastax.bdp.cassandra.auth.DseAuthenticator' ?
30
+ 'PLAIN' :
31
+ challenge_response('PLAIN-START')
32
+ end
33
+
34
+ def challenge_response(token)
35
+ "\x00#{@username}\x00#{@password}"
36
+ end
37
+
38
+ def authentication_successful(token)
39
+ end
40
+ end
41
+
42
+ # @param username [String] username to use for authentication to Cassandra
43
+ # @param password [String] password to use for authentication to Cassandra
44
+ def initialize(username, password)
45
+ @username = username
46
+ @password = password
47
+ end
48
+
49
+ # @private
50
+ def create_authenticator(authentication_class, host)
51
+ Authenticator.new(authentication_class, @username, @password)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,99 @@
1
+ # encoding: utf-8
2
+
3
+ #--
4
+ # Copyright (C) 2016 DataStax Inc.
5
+ #
6
+ # This software can be used solely with DataStax Enterprise. Please consult the license at
7
+ # http://www.datastax.com/terms/datastax-dse-driver-license-terms
8
+ #++
9
+
10
+ module Dse
11
+ # Cluster represents a DSE cluster. It serves as a {Dse::Session session factory} and a collection of metadata.
12
+ # It wraps a {http://dsdocs30/api/cassandra/cluster Cassandra::Cluster} and exposes all of its functionality.
13
+ class Cluster
14
+ # @return [Dse::Graph::Options] default graph options used by queries on this cluster.
15
+ attr_reader :graph_options
16
+
17
+ # @private
18
+ def initialize(logger,
19
+ io_reactor,
20
+ executor,
21
+ control_connection,
22
+ cluster_registry,
23
+ cluster_schema,
24
+ cluster_metadata,
25
+ execution_options,
26
+ connection_options,
27
+ load_balancing_policy,
28
+ reconnection_policy,
29
+ retry_policy,
30
+ address_resolution_policy,
31
+ connector,
32
+ futures_factory,
33
+ timestamp_generator)
34
+ @delegate_cluster = Cassandra::Cluster.new(logger,
35
+ io_reactor,
36
+ executor,
37
+ control_connection,
38
+ cluster_registry,
39
+ cluster_schema,
40
+ cluster_metadata,
41
+ execution_options,
42
+ connection_options,
43
+ load_balancing_policy,
44
+ reconnection_policy,
45
+ retry_policy,
46
+ address_resolution_policy,
47
+ connector,
48
+ futures_factory,
49
+ timestamp_generator)
50
+ @graph_options = Dse::Graph::Options.new
51
+
52
+ # We need the futures factory ourselves for async error reporting and potentially for our
53
+ # own async processing independent of the C* driver.
54
+ @futures = futures_factory
55
+ end
56
+
57
+ # Delegates to {http://docs.datastax.com/en/developer/ruby-driver/3.0/supplemental/api/cassandra/cluster/?local=true&nav=toc#connect_async-instance_method
58
+ # Cassandra::Cluster#connect_async}
59
+ # to connect asynchronously to a cluster, but returns a future that will resolve to a DSE session rather than
60
+ # Cassandra session.
61
+ #
62
+ # @param keyspace [String] optional keyspace to scope session to
63
+ #
64
+ # @return [Cassandra::Future<Dse::Session>]
65
+ def connect_async(keyspace = nil)
66
+ future = @delegate_cluster.connect_async(keyspace)
67
+ # We want to actually return a DSE session upon successful connection.
68
+ future.then do |cassandra_session|
69
+ Dse::Session.new(cassandra_session, @graph_options, @futures)
70
+ end
71
+ end
72
+
73
+ # Synchronous variant of {#connect_async}.
74
+ #
75
+ # @param keyspace [String] optional keyspace to scope the session to
76
+ #
77
+ # @return [Dse::Session]
78
+ def connect(keyspace = nil)
79
+ connect_async(keyspace).get
80
+ end
81
+
82
+ #### The following methods handle arbitrary delegation to the underlying cluster object. ####
83
+ protected
84
+
85
+ # @private
86
+ def method_missing(method_name, *args, &block)
87
+ # If we get here, we don't have a method of our own. Forward the request to the delegate_cluster.
88
+ # If it returns itself, we will coerce the result to return our *self* instead.
89
+
90
+ result = @delegate_cluster.send(method_name, *args, &block)
91
+ (result == @delegate_cluster) ? self : result
92
+ end
93
+
94
+ # @private
95
+ def respond_to?(method, include_private = false)
96
+ super || @delegate_cluster.respond_to?(method, include_private)
97
+ end
98
+ end
99
+ end