mongo 2.0.4 → 2.0.5

Sign up to get free protection for your applications and to get access to all the features.
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