mongo 2.0.4 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongo/address.rb +1 -1
  5. data/lib/mongo/address/ipv4.rb +8 -4
  6. data/lib/mongo/address/ipv6.rb +8 -4
  7. data/lib/mongo/address/unix.rb +2 -2
  8. data/lib/mongo/auth/user/view.rb +5 -1
  9. data/lib/mongo/bulk_write/bulk_writable.rb +5 -0
  10. data/lib/mongo/bulk_write/deletable.rb +0 -4
  11. data/lib/mongo/bulk_write/insertable.rb +0 -4
  12. data/lib/mongo/client.rb +30 -2
  13. data/lib/mongo/collection/view/aggregation.rb +17 -4
  14. data/lib/mongo/collection/view/map_reduce.rb +19 -2
  15. data/lib/mongo/database.rb +12 -0
  16. data/lib/mongo/database/view.rb +12 -0
  17. data/lib/mongo/grid/fs.rb +5 -5
  18. data/lib/mongo/loggable.rb +5 -3
  19. data/lib/mongo/logger.rb +21 -5
  20. data/lib/mongo/operation/aggregate.rb +18 -11
  21. data/lib/mongo/operation/aggregate/result.rb +16 -1
  22. data/lib/mongo/operation/map_reduce.rb +7 -9
  23. data/lib/mongo/operation/read/query.rb +1 -1
  24. data/lib/mongo/operation/read_preferrable.rb +11 -5
  25. data/lib/mongo/operation/result.rb +5 -1
  26. data/lib/mongo/operation/write.rb +1 -0
  27. data/lib/mongo/operation/write/command.rb +1 -0
  28. data/lib/mongo/operation/write/command/update_user.rb +43 -0
  29. data/lib/mongo/operation/write/update_user.rb +75 -0
  30. data/lib/mongo/protocol/reply.rb +12 -0
  31. data/lib/mongo/server/connection_pool/queue.rb +3 -3
  32. data/lib/mongo/socket.rb +22 -11
  33. data/lib/mongo/socket/ssl.rb +6 -3
  34. data/lib/mongo/version.rb +1 -1
  35. data/spec/mongo/auth/user/view_spec.rb +42 -0
  36. data/spec/mongo/client_spec.rb +19 -0
  37. data/spec/mongo/cluster_spec.rb +2 -1
  38. data/spec/mongo/collection/view/aggregation_spec.rb +34 -1
  39. data/spec/mongo/collection/view/map_reduce_spec.rb +92 -0
  40. data/spec/mongo/collection/view_spec.rb +14 -11
  41. data/spec/mongo/collection_spec.rb +13 -0
  42. data/spec/mongo/database_spec.rb +29 -0
  43. data/spec/mongo/grid/fs_spec.rb +32 -1
  44. data/spec/mongo/loggable_spec.rb +2 -1
  45. data/spec/mongo/operation/read/query_spec.rb +19 -0
  46. data/spec/mongo/operation/read_preferrable_spec.rb +192 -0
  47. data/spec/mongo/operation/write/update_user_spec.rb +46 -0
  48. data/spec/mongo/server/connection_pool/queue_spec.rb +4 -0
  49. data/spec/mongo/socket/ssl_spec.rb +21 -1
  50. data/spec/spec_helper.rb +4 -0
  51. data/spec/support/authorization.rb +6 -4
  52. data/spec/support/shared/operation.rb +12 -0
  53. metadata +9 -3
  54. metadata.gz.sig +0 -0
@@ -31,6 +31,16 @@ module Mongo
31
31
  # @since 2.0.0
32
32
  CURSOR_ID = 'id'.freeze
33
33
 
34
+ # The field name for the aggregation explain information.
35
+ #
36
+ # @since 2.0.5
37
+ EXPLAIN = 'stages'.freeze
38
+
39
+ # The legacy field name for the aggregation explain information.
40
+ #
41
+ # @since 2.0.5
42
+ EXPLAIN_LEGACY = 'serverPipeline'.freeze
43
+
34
44
  # The field name for the first batch of a cursor.
35
45
  #
36
46
  # @since 2.0.0
@@ -70,11 +80,16 @@ module Mongo
70
80
  #
71
81
  # @since 2.0.0
72
82
  def documents
73
- reply.documents[0][RESULT] || cursor_document[FIRST_BATCH]
83
+ reply.documents[0][RESULT] || explain_document ||
84
+ cursor_document[FIRST_BATCH]
74
85
  end
75
86
 
76
87
  private
77
88
 
89
+ def explain_document
90
+ first_document[EXPLAIN] || first_document[EXPLAIN_LEGACY]
91
+ end
92
+
78
93
  def cursor_document
79
94
  @cursor_document ||= reply.documents[0][CURSOR]
80
95
  end
@@ -63,7 +63,7 @@ module Mongo
63
63
  #
64
64
  # @since 2.0.0
65
65
  def execute(context)
66
- unless context.standalone? || context.mongos? || context.primary? || secondary_ok?
66
+ unless valid_context?(context)
67
67
  raise Error::NeedPrimaryServer.new(ERROR_MESSAGE)
68
68
  end
69
69
  execute_message(context)
@@ -71,21 +71,19 @@ module Mongo
71
71
 
72
72
  private
73
73
 
74
+ def valid_context?(context)
75
+ context.standalone? || context.mongos? || context.primary? || secondary_ok?
76
+ end
77
+
74
78
  def execute_message(context)
75
79
  context.with_connection do |connection|
76
80
  Result.new(connection.dispatch([ message(context) ])).validate!
77
81
  end
78
82
  end
79
83
 
80
- # Whether this operation can be executed on a replica set secondary server.
81
- # The map reduce operation may not be executed on a secondary if the user has specified
82
- # an output collection to which the results will be written.
83
- #
84
- # @return [ true, false ] Whether the operation can be executed on a secondary.
85
- #
86
- # @since 2.0.0
87
84
  def secondary_ok?
88
- selector[:out] == 'inline'
85
+ selector[:out].respond_to?(:keys) &&
86
+ selector[:out].keys.first.to_s.downcase == 'inline'
89
87
  end
90
88
 
91
89
  def query_coll
@@ -58,7 +58,7 @@ module Mongo
58
58
 
59
59
  def execute_message(context)
60
60
  context.with_connection do |connection|
61
- Result.new(connection.dispatch([ message(context) ]))
61
+ Result.new(connection.dispatch([ message(context) ])).validate!
62
62
  end
63
63
  end
64
64
 
@@ -20,21 +20,27 @@ module Mongo
20
20
  # @since 2.0.0
21
21
  module ReadPreferrable
22
22
 
23
+ # The constant for slave ok flags.
24
+ #
25
+ # @since 2.0.5
26
+ SLAVE_OK = :slave_ok
27
+
23
28
  private
24
29
 
25
30
  def update_selector(context)
26
31
  if context.mongos? && read_pref = read.to_mongos
27
- selector.merge(:$readPreference => read_pref)
32
+ sel = selector[:$query] ? selector : { :$query => selector }
33
+ sel.merge(:$readPreference => read_pref)
28
34
  else
29
35
  selector
30
36
  end
31
37
  end
32
38
 
33
39
  def update_options(context)
34
- if context.slave_ok?
35
- options.merge(flags: [:slave_ok])
36
- elsif !context.mongos? && read.slave_ok?
37
- options.merge(flags: [:slave_ok])
40
+ if context.slave_ok? || (!context.mongos? && read.slave_ok?)
41
+ options.dup.tap do |opts|
42
+ (opts[:flags] ||= []) << SLAVE_OK
43
+ end
38
44
  else
39
45
  options
40
46
  end
@@ -192,7 +192,7 @@ module Mongo
192
192
  if first_document.has_key?(OK)
193
193
  first_document[OK] == 1 && parser.message.empty?
194
194
  else
195
- parser.message.empty?
195
+ !query_failure? && parser.message.empty?
196
196
  end
197
197
  end
198
198
 
@@ -254,6 +254,10 @@ module Mongo
254
254
  def first_document
255
255
  @first_document ||= first || BSON::Document.new
256
256
  end
257
+
258
+ def query_failure?
259
+ replies.first && replies.first.query_failure?
260
+ end
257
261
  end
258
262
  end
259
263
  end
@@ -20,5 +20,6 @@ require 'mongo/operation/write/update'
20
20
  require 'mongo/operation/write/create_index'
21
21
  require 'mongo/operation/write/drop_index'
22
22
  require 'mongo/operation/write/create_user'
23
+ require 'mongo/operation/write/update_user'
23
24
  require 'mongo/operation/write/remove_user'
24
25
  require 'mongo/operation/write/command'
@@ -19,4 +19,5 @@ require 'mongo/operation/write/command/update'
19
19
  require 'mongo/operation/write/command/drop_index'
20
20
  require 'mongo/operation/write/command/create_index'
21
21
  require 'mongo/operation/write/command/create_user'
22
+ require 'mongo/operation/write/command/update_user'
22
23
  require 'mongo/operation/write/command/remove_user'
@@ -0,0 +1,43 @@
1
+
2
+ # Copyright (C) 2014-2015 MongoDB, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ module Mongo
17
+ module Operation
18
+ module Write
19
+ module Command
20
+
21
+ # Update user command on non-legacy servers.
22
+ #
23
+ # @since 2.0.0
24
+ class UpdateUser
25
+ include Specifiable
26
+ include Executable
27
+ include Writable
28
+
29
+ private
30
+
31
+ # The query selector for this update user command operation.
32
+ #
33
+ # @return [ Hash ] The selector describing this update user operation.
34
+ #
35
+ # @since 2.0.0
36
+ def selector
37
+ { :updateUser => user.name, :digestPassword => false }.merge(user.spec)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,75 @@
1
+
2
+ # Copyright (C) 2014-2015 MongoDB, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ module Mongo
17
+ module Operation
18
+ module Write
19
+
20
+ # A MongoDB update user operation.
21
+ #
22
+ # @example Initialize the operation.
23
+ # Write::UpdateUser.new(:db_name => 'test', :user => user)
24
+ #
25
+ # @param [ Hash ] spec The specifications for the update.
26
+ #
27
+ # @option spec :user [ Auth::User ] The user to update.
28
+ # @option spec :db_name [ String ] The name of the database.
29
+ #
30
+ # @since 2.0.0
31
+ class UpdateUser
32
+ include Executable
33
+ include Specifiable
34
+
35
+ # Execute the operation.
36
+ #
37
+ # @note Updating users behaves different on 2.7+, 2.6.x, and
38
+ # 2.4- so we need to break this out into separate operations.
39
+ #
40
+ # @example Execute the operation.
41
+ # operation.execute(context)
42
+ #
43
+ # @param [ Mongo::Server::Context ] context The context for this operation.
44
+ #
45
+ # @return [ Result ] The operation result.
46
+ #
47
+ # @since 2.0.0
48
+ def execute(context)
49
+ if context.features.write_command_enabled?
50
+ execute_write_command(context)
51
+ else
52
+ execute_message(context)
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def execute_write_command(context)
59
+ Result.new(Command::UpdateUser.new(spec).execute(context)).validate!
60
+ end
61
+
62
+ def execute_message(context)
63
+ context.with_connection do |connection|
64
+ Result.new(connection.dispatch([ message, gle ].compact)).validate!
65
+ end
66
+ end
67
+
68
+ def message
69
+ user_spec = { user: user.name }.merge(user.spec)
70
+ Protocol::Update.new(db_name, Auth::User::COLLECTION, { user: user.name }, user_spec)
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -26,6 +26,18 @@ module Mongo
26
26
  # @api semipublic
27
27
  class Reply < Message
28
28
 
29
+ # Determine if the reply had a query failure flag.
30
+ #
31
+ # @example Did the reply have a query failure.
32
+ # reply.query_failure?
33
+ #
34
+ # @return [ true, false ] If the query failed.
35
+ #
36
+ # @since 2.0.5
37
+ def query_failure?
38
+ flags.include?(:query_failure)
39
+ end
40
+
29
41
  private
30
42
 
31
43
  # The operation code required to specify a Reply message.
@@ -73,7 +73,7 @@ module Mongo
73
73
  # @since 2.0.0
74
74
  def enqueue(connection)
75
75
  mutex.synchronize do
76
- queue.push(connection)
76
+ queue.unshift(connection)
77
77
  resource.broadcast
78
78
  end
79
79
  end
@@ -96,7 +96,7 @@ module Mongo
96
96
  @block = block
97
97
  @connections = 0
98
98
  @options = options
99
- @queue = Array.new(min_size, create_connection)
99
+ @queue = Array.new(min_size) { create_connection }
100
100
  @mutex = Mutex.new
101
101
  @resource = ConditionVariable.new
102
102
  end
@@ -155,7 +155,7 @@ module Mongo
155
155
  def dequeue_connection
156
156
  deadline = Time.now + wait_timeout
157
157
  loop do
158
- return queue.delete_at(0) unless queue.empty?
158
+ return queue.pop unless queue.empty?
159
159
  connection = create_connection
160
160
  return connection if connection
161
161
  wait_for_next!(deadline)
@@ -56,13 +56,12 @@ module Mongo
56
56
  #
57
57
  # @since 2.0.0
58
58
  def alive?
59
- if Kernel::select([ socket ], nil, [ socket ], 0)
60
- !eof? rescue false
59
+ sock_arr = [ @socket ]
60
+ if Kernel::select(sock_arr, nil, sock_arr, 0)
61
+ eof?
61
62
  else
62
63
  true
63
64
  end
64
- rescue IOError
65
- false
66
65
  end
67
66
 
68
67
  # Close the socket.
@@ -74,7 +73,7 @@ module Mongo
74
73
  #
75
74
  # @since 2.0.0
76
75
  def close
77
- socket.close rescue true
76
+ @socket.close rescue true
78
77
  true
79
78
  end
80
79
 
@@ -89,7 +88,7 @@ module Mongo
89
88
  #
90
89
  # @since 2.0.0
91
90
  def gets(*args)
92
- handle_errors { socket.gets(*args) }
91
+ handle_errors { @socket.gets(*args) }
93
92
  end
94
93
 
95
94
  # Create the new socket for the provided family - ipv4, piv6, or unix.
@@ -107,7 +106,7 @@ module Mongo
107
106
  end
108
107
 
109
108
  # Will read all data from the socket for the provided number of bytes.
110
- # If less data is returned than requested, an exception will be raised.
109
+ # If no data is returned, an exception will be raised.
111
110
  #
112
111
  # @example Read all the requested data from the socket.
113
112
  # socket.read(4096)
@@ -122,8 +121,11 @@ module Mongo
122
121
  def read(length)
123
122
  handle_errors do
124
123
  data = read_from_socket(length)
124
+ raise IOError unless (data.length > 0 || length == 0)
125
125
  while data.length < length
126
- data << read_from_socket(length - data.length)
126
+ chunk = read_from_socket(length - data.length)
127
+ raise IOError unless (chunk.length > 0 || length == 0)
128
+ data << chunk
127
129
  end
128
130
  data
129
131
  end
@@ -138,7 +140,7 @@ module Mongo
138
140
  #
139
141
  # @since 2.0.0
140
142
  def readbyte
141
- handle_errors { socket.readbyte }
143
+ handle_errors { @socket.readbyte }
142
144
  end
143
145
 
144
146
  # Writes data to the socket instance.
@@ -152,13 +154,22 @@ module Mongo
152
154
  #
153
155
  # @since 2.0.0
154
156
  def write(*args)
155
- handle_errors { socket.write(*args) }
157
+ handle_errors { @socket.write(*args) }
158
+ end
159
+
160
+ # Tests if this socket has reached EOF. Primarily used for liveness checks.
161
+ #
162
+ # @since 2.0.5
163
+ def eof?
164
+ @socket.eof?
165
+ rescue IOError, SystemCallError => e
166
+ true
156
167
  end
157
168
 
158
169
  private
159
170
 
160
171
  def read_from_socket(length)
161
- socket.read(length) || String.new
172
+ @socket.read(length) || String.new
162
173
  end
163
174
 
164
175
  def set_socket_options(sock)
@@ -29,6 +29,9 @@ module Mongo
29
29
  # @return [ String ] host The host to connect to.
30
30
  attr_reader :host
31
31
 
32
+ # @return [ String ] host_name The original host name.
33
+ attr_reader :host_name
34
+
32
35
  # @return [ Hash ] The ssl options.
33
36
  attr_reader :options
34
37
 
@@ -72,8 +75,8 @@ module Mongo
72
75
  # @param [ Hash ] options The ssl options.
73
76
  #
74
77
  # @since 2.0.0
75
- def initialize(host, port, timeout, family, options = {})
76
- @host, @port, @timeout, @options = host, port, timeout, options
78
+ def initialize(host, port, host_name, timeout, family, options = {})
79
+ @host, @port, @host_name, @timeout, @options = host, port, host_name, timeout, options
77
80
  @context = create_context(options)
78
81
  @family = family
79
82
  @tcp_socket = ::Socket.new(family, SOCK_STREAM, 0)
@@ -117,7 +120,7 @@ module Mongo
117
120
 
118
121
  def verify_certificate!(socket)
119
122
  if context.verify_mode == OpenSSL::SSL::VERIFY_PEER
120
- unless OpenSSL::SSL.verify_certificate_identity(socket.peer_cert, host)
123
+ unless OpenSSL::SSL.verify_certificate_identity(socket.peer_cert, host_name)
121
124
  raise Error::SocketError, 'SSL handshake failed due to a hostname mismatch.'
122
125
  end
123
126
  end
@@ -17,5 +17,5 @@ module Mongo
17
17
  # The current version of the driver.
18
18
  #
19
19
  # @since 2.0.0
20
- VERSION = '2.0.4'.freeze
20
+ VERSION = '2.0.5'.freeze
21
21
  end
@@ -36,6 +36,48 @@ describe Mongo::Auth::User::View do
36
36
  end
37
37
  end
38
38
 
39
+ describe '#update' do
40
+
41
+ before do
42
+ view.create(
43
+ 'durran',
44
+ password: 'password', roles: [ Mongo::Auth::Roles::READ_WRITE ]
45
+ )
46
+ end
47
+
48
+ after do
49
+ view.remove('durran')
50
+ end
51
+
52
+ context 'when a user password is updated' do
53
+
54
+ let!(:response) do
55
+ view.update(
56
+ 'durran',
57
+ password: '123', roles: [ Mongo::Auth::Roles::READ_WRITE ]
58
+ )
59
+ end
60
+
61
+ it 'updates the password' do
62
+ expect(response).to be_successful
63
+ end
64
+ end
65
+
66
+ context 'when the roles of a user are updated' do
67
+
68
+ let!(:response) do
69
+ view.update(
70
+ 'durran',
71
+ password: 'password', roles: [ Mongo::Auth::Roles::READ ]
72
+ )
73
+ end
74
+
75
+ it 'updates the roles' do
76
+ expect(response).to be_successful
77
+ end
78
+ end
79
+ end
80
+
39
81
  describe '#remove' do
40
82
 
41
83
  context 'when user removal was successful' do