mongo 2.18.1 → 2.18.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +1 -1
  4. data/lib/mongo/bulk_write.rb +6 -4
  5. data/lib/mongo/client.rb +1 -1
  6. data/lib/mongo/collection/view/iterable.rb +15 -0
  7. data/lib/mongo/collection/view/writable.rb +0 -2
  8. data/lib/mongo/collection/view.rb +1 -0
  9. data/lib/mongo/collection.rb +150 -45
  10. data/lib/mongo/crypt/auto_encrypter.rb +1 -0
  11. data/lib/mongo/crypt/explicit_encrypter.rb +1 -0
  12. data/lib/mongo/crypt/kms.rb +0 -1
  13. data/lib/mongo/crypt.rb +11 -0
  14. data/lib/mongo/error/invalid_read_option.rb +1 -1
  15. data/lib/mongo/grid/file/chunk.rb +2 -1
  16. data/lib/mongo/grid/file/info.rb +2 -1
  17. data/lib/mongo/operation/aggregate.rb +1 -2
  18. data/lib/mongo/operation/collections_info.rb +3 -15
  19. data/lib/mongo/operation/command.rb +1 -2
  20. data/lib/mongo/operation/count.rb +1 -2
  21. data/lib/mongo/operation/create.rb +1 -2
  22. data/lib/mongo/operation/create_index.rb +1 -2
  23. data/lib/mongo/operation/create_user.rb +1 -2
  24. data/lib/mongo/operation/delete.rb +0 -1
  25. data/lib/mongo/operation/distinct.rb +1 -2
  26. data/lib/mongo/operation/drop.rb +1 -2
  27. data/lib/mongo/operation/drop_database.rb +1 -2
  28. data/lib/mongo/operation/drop_index.rb +1 -2
  29. data/lib/mongo/operation/explain.rb +1 -3
  30. data/lib/mongo/operation/find/builder.rb +0 -1
  31. data/lib/mongo/operation/find.rb +1 -3
  32. data/lib/mongo/operation/get_more.rb +1 -3
  33. data/lib/mongo/operation/indexes.rb +1 -17
  34. data/lib/mongo/operation/insert.rb +0 -1
  35. data/lib/mongo/operation/kill_cursors.rb +1 -2
  36. data/lib/mongo/operation/list_collections.rb +1 -2
  37. data/lib/mongo/operation/map_reduce.rb +1 -2
  38. data/lib/mongo/operation/parallel_scan.rb +1 -2
  39. data/lib/mongo/operation/remove_user.rb +1 -2
  40. data/lib/mongo/operation/shared/{polymorphic_operation.rb → op_msg_executable.rb} +11 -6
  41. data/lib/mongo/operation/update.rb +0 -1
  42. data/lib/mongo/operation/update_user.rb +1 -2
  43. data/lib/mongo/operation/users_info.rb +1 -2
  44. data/lib/mongo/operation/write_command.rb +1 -2
  45. data/lib/mongo/operation.rb +1 -3
  46. data/lib/mongo/protocol/bit_vector.rb +3 -1
  47. data/lib/mongo/protocol/caching_hash.rb +3 -20
  48. data/lib/mongo/protocol/message.rb +4 -8
  49. data/lib/mongo/protocol/msg.rb +1 -0
  50. data/lib/mongo/protocol/serializers.rb +24 -17
  51. data/lib/mongo/protocol.rb +0 -3
  52. data/lib/mongo/query_cache.rb +20 -20
  53. data/lib/mongo/server/app_metadata/environment.rb +255 -0
  54. data/lib/mongo/server/app_metadata/truncator.rb +142 -0
  55. data/lib/mongo/server/app_metadata.rb +29 -42
  56. data/lib/mongo/session.rb +1 -1
  57. data/lib/mongo/version.rb +1 -1
  58. data/spec/integration/command_spec.rb +1 -23
  59. data/spec/integration/connection/faas_env_spec.rb +63 -0
  60. data/spec/integration/find_options_spec.rb +227 -0
  61. data/spec/integration/ocsp_verifier_spec.rb +1 -1
  62. data/spec/lite_spec_helper.rb +9 -0
  63. data/spec/mongo/address_spec.rb +1 -1
  64. data/spec/mongo/client_construction_spec.rb +7 -7
  65. data/spec/mongo/client_spec.rb +1 -9
  66. data/spec/mongo/cluster_spec.rb +2 -2
  67. data/spec/mongo/collection_crud_spec.rb +56 -0
  68. data/spec/mongo/collection_spec.rb +11 -1
  69. data/spec/mongo/crypt/kms_spec.rb +12 -9
  70. data/spec/mongo/crypt_spec.rb +21 -0
  71. data/spec/mongo/index/view_spec.rb +1 -0
  72. data/spec/mongo/protocol/caching_hash_spec.rb +0 -45
  73. data/spec/mongo/protocol/msg_spec.rb +2 -4
  74. data/spec/mongo/server/app_metadata/environment_spec.rb +193 -0
  75. data/spec/mongo/server/app_metadata/truncator_spec.rb +158 -0
  76. data/spec/mongo/server/app_metadata_spec.rb +33 -47
  77. data/spec/mongo/socket/ssl_spec.rb +2 -8
  78. data/spec/runners/crud/requirement.rb +2 -2
  79. data/spec/shared/lib/mrss/docker_runner.rb +4 -0
  80. data/spec/shared/lib/mrss/lite_constraints.rb +8 -0
  81. data/spec/shared/lib/mrss/server_version_registry.rb +16 -23
  82. data/spec/shared/share/Dockerfile.erb +24 -19
  83. data/spec/shared/shlib/server.sh +31 -7
  84. data/spec/shared/shlib/set_env.sh +4 -4
  85. data/spec/solo/clean_exit_spec.rb +3 -10
  86. data/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml +15 -6
  87. data/spec/spec_tests/data/command_monitoring_unified/redacted-commands.yml +8 -0
  88. data/spec/support/aws_utils.rb +3 -3
  89. data/spec/support/certificates/atlas-ocsp-ca.crt +67 -67
  90. data/spec/support/certificates/atlas-ocsp.crt +103 -103
  91. data/spec/support/shared/app_metadata.rb +14 -2
  92. data.tar.gz.sig +0 -0
  93. metadata +1203 -1239
  94. metadata.gz.sig +0 -0
  95. data/lib/mongo/operation/aggregate/command.rb +0 -55
  96. data/lib/mongo/operation/collections_info/command.rb +0 -48
  97. data/lib/mongo/operation/command/command.rb +0 -41
  98. data/lib/mongo/operation/count/command.rb +0 -47
  99. data/lib/mongo/operation/create/command.rb +0 -47
  100. data/lib/mongo/operation/create_index/command.rb +0 -61
  101. data/lib/mongo/operation/create_user/command.rb +0 -46
  102. data/lib/mongo/operation/delete/command.rb +0 -52
  103. data/lib/mongo/operation/distinct/command.rb +0 -47
  104. data/lib/mongo/operation/drop/command.rb +0 -41
  105. data/lib/mongo/operation/drop_database/command.rb +0 -41
  106. data/lib/mongo/operation/drop_index/command.rb +0 -45
  107. data/lib/mongo/operation/explain/command.rb +0 -58
  108. data/lib/mongo/operation/explain/legacy.rb +0 -52
  109. data/lib/mongo/operation/find/builder/legacy.rb +0 -123
  110. data/lib/mongo/operation/find/command.rb +0 -51
  111. data/lib/mongo/operation/find/legacy/result.rb +0 -46
  112. data/lib/mongo/operation/find/legacy.rb +0 -52
  113. data/lib/mongo/operation/get_more/command.rb +0 -43
  114. data/lib/mongo/operation/get_more/legacy.rb +0 -39
  115. data/lib/mongo/operation/indexes/command.rb +0 -42
  116. data/lib/mongo/operation/indexes/legacy.rb +0 -48
  117. data/lib/mongo/operation/insert/command.rb +0 -55
  118. data/lib/mongo/operation/kill_cursors/command.rb +0 -48
  119. data/lib/mongo/operation/list_collections/command.rb +0 -46
  120. data/lib/mongo/operation/map_reduce/command.rb +0 -51
  121. data/lib/mongo/operation/parallel_scan/command.rb +0 -57
  122. data/lib/mongo/operation/remove_user/command.rb +0 -46
  123. data/lib/mongo/operation/shared/op_msg_or_command.rb +0 -41
  124. data/lib/mongo/operation/shared/op_msg_or_find_command.rb +0 -44
  125. data/lib/mongo/operation/update/command.rb +0 -53
  126. data/lib/mongo/operation/update_user/command.rb +0 -45
  127. data/lib/mongo/operation/users_info/command.rb +0 -46
  128. data/lib/mongo/operation/write_command/command.rb +0 -51
  129. data/lib/mongo/protocol/delete.rb +0 -172
  130. data/lib/mongo/protocol/insert.rb +0 -181
  131. data/lib/mongo/protocol/update.rb +0 -214
  132. data/spec/mongo/operation/delete/command_spec.rb +0 -115
  133. data/spec/mongo/operation/find/legacy_spec.rb +0 -131
  134. data/spec/mongo/operation/get_more_spec.rb +0 -63
  135. data/spec/mongo/operation/insert/command_spec.rb +0 -118
  136. data/spec/mongo/operation/update/command_spec.rb +0 -122
  137. data/spec/mongo/protocol/delete_spec.rb +0 -185
  138. data/spec/mongo/protocol/insert_spec.rb +0 -179
  139. data/spec/mongo/protocol/update_spec.rb +0 -204
@@ -51,9 +51,11 @@ module Mongo
51
51
  #
52
52
  # @param buffer [ String ] Buffer to receive the serialized value.
53
53
  # @param value [ String ] Header value to be serialized.
54
+ # @param [ true, false ] validating_keys Whether keys should be validated when serializing.
55
+ # This option is deprecated and will not be used. It will removed in version 3.0.
54
56
  #
55
57
  # @return [ String ] Buffer with serialized value.
56
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
58
+ def self.serialize(buffer, value, validating_keys = nil)
57
59
  buffer.put_bytes(value.pack(HEADER_PACK))
58
60
  end
59
61
 
@@ -80,7 +82,7 @@ module Mongo
80
82
  # @param value [ String ] The string to be serialized.
81
83
  #
82
84
  # @return [ String ] Buffer with serialized value.
83
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
85
+ def self.serialize(buffer, value, validating_keys = nil)
84
86
  buffer.put_cstring(value)
85
87
  end
86
88
  end
@@ -96,7 +98,7 @@ module Mongo
96
98
  # @param value [ Fixnum ] Ignored value.
97
99
  #
98
100
  # @return [ String ] Buffer with serialized value.
99
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
101
+ def self.serialize(buffer, value, validating_keys = nil)
100
102
  buffer.put_int32(ZERO)
101
103
  end
102
104
  end
@@ -112,7 +114,7 @@ module Mongo
112
114
  # @param value [ Integer | BSON::Int32 ] 32-bit integer to be serialized.
113
115
  #
114
116
  # @return [String] Buffer with serialized value.
115
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
117
+ def self.serialize(buffer, value, validating_keys = nil)
116
118
  if value.is_a?(BSON::Int32)
117
119
  if value.respond_to?(:value)
118
120
  # bson-ruby >= 4.6.0
@@ -146,7 +148,7 @@ module Mongo
146
148
  # @param value [ Integer | BSON::Int64 ] 64-bit integer to be serialized.
147
149
  #
148
150
  # @return [ String ] Buffer with serialized value.
149
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
151
+ def self.serialize(buffer, value, validating_keys = nil)
150
152
  if value.is_a?(BSON::Int64)
151
153
  if value.respond_to?(:value)
152
154
  # bson-ruby >= 4.6.0
@@ -182,19 +184,20 @@ module Mongo
182
184
  # @param [ Array<Hash, BSON::Document> ] value The sections to be serialized.
183
185
  # @param [ Fixnum ] max_bson_size The max bson size of documents in the sections.
184
186
  # @param [ true, false ] validating_keys Whether to validate document keys.
187
+ # This option is deprecated and will not be used. It will removed in version 3.0.
185
188
  #
186
189
  # @return [ BSON::ByteBuffer ] Buffer with serialized value.
187
190
  #
188
191
  # @since 2.5.0
189
- def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
192
+ def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
190
193
  value.each do |section|
191
194
  case section[:type]
192
195
  when PayloadZero::TYPE
193
- PayloadZero.serialize(buffer, section[:payload], max_bson_size, false)
196
+ PayloadZero.serialize(buffer, section[:payload], max_bson_size)
194
197
  when nil
195
- PayloadZero.serialize(buffer, section[:payload], max_bson_size, false)
198
+ PayloadZero.serialize(buffer, section[:payload], max_bson_size)
196
199
  when PayloadOne::TYPE
197
- PayloadOne.serialize(buffer, section[:payload], max_bson_size, validating_keys)
200
+ PayloadOne.serialize(buffer, section[:payload], max_bson_size)
198
201
  else
199
202
  raise Error::UnknownPayloadType.new(section[:type])
200
203
  end
@@ -259,13 +262,14 @@ module Mongo
259
262
  # @param [ BSON::Document, Hash ] value The object to serialize.
260
263
  # @param [ Fixnum ] max_bson_size The max bson size of documents in the section.
261
264
  # @param [ true, false ] validating_keys Whether to validate document keys.
265
+ # This option is deprecated and will not be used. It will removed in version 3.0.
262
266
  #
263
267
  # @return [ BSON::ByteBuffer ] Buffer with serialized value.
264
268
  #
265
269
  # @since 2.5.0
266
- def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
270
+ def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
267
271
  buffer.put_byte(TYPE_BYTE)
268
- Serializers::Document.serialize(buffer, value, max_bson_size, validating_keys)
272
+ Serializers::Document.serialize(buffer, value, max_bson_size)
269
273
  end
270
274
 
271
275
  # Deserializes a section of payload type 0 of an OP_MSG from the IO stream.
@@ -307,17 +311,18 @@ module Mongo
307
311
  # @param [ BSON::Document, Hash ] value The object to serialize.
308
312
  # @param [ Fixnum ] max_bson_size The max bson size of documents in the section.
309
313
  # @param [ true, false ] validating_keys Whether to validate document keys.
314
+ # This option is deprecated and will not be used. It will removed in version 3.0.
310
315
  #
311
316
  # @return [ BSON::ByteBuffer ] Buffer with serialized value.
312
317
  #
313
318
  # @since 2.5.0
314
- def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
319
+ def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
315
320
  buffer.put_byte(TYPE_BYTE)
316
321
  start = buffer.length
317
322
  buffer.put_int32(0) # hold for size
318
323
  buffer.put_cstring(value[:identifier])
319
324
  value[:sequence].each do |document|
320
- Document.serialize(buffer, document, max_bson_size, validating_keys)
325
+ Document.serialize(buffer, document, max_bson_size)
321
326
  end
322
327
  buffer.replace_int32(start, buffer.length - start)
323
328
  end
@@ -356,9 +361,9 @@ module Mongo
356
361
  # @param value [ Hash ] Document to serialize as BSON.
357
362
  #
358
363
  # @return [ String ] Buffer with serialized value.
359
- def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
364
+ def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
360
365
  start_size = buffer.length
361
- value.to_bson(buffer, validating_keys)
366
+ value.to_bson(buffer)
362
367
  serialized_size = buffer.length - start_size
363
368
  if max_bson_size && serialized_size > max_bson_size
364
369
  raise Error::MaxBSONSize,
@@ -401,11 +406,12 @@ module Mongo
401
406
  # @param [ BSON::ByteBuffer ] buffer Buffer to receive the single byte.
402
407
  # @param [ String ] value The byte to write to the buffer.
403
408
  # @param [ true, false ] validating_keys Whether to validate keys.
409
+ # This option is deprecated and will not be used. It will removed in version 3.0.
404
410
  #
405
411
  # @return [ BSON::ByteBuffer ] Buffer with serialized value.
406
412
  #
407
413
  # @since 2.5.0
408
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
414
+ def self.serialize(buffer, value, validating_keys = nil)
409
415
  buffer.put_byte(value)
410
416
  end
411
417
 
@@ -432,11 +438,12 @@ module Mongo
432
438
  # @param [ BSON::ByteBuffer ] buffer Buffer to receive the bytes.
433
439
  # @param [ String ] value The bytes to write to the buffer.
434
440
  # @param [ true, false ] validating_keys Whether to validate keys.
441
+ # This option is deprecated and will not be used. It will removed in version 3.0.
435
442
  #
436
443
  # @return [ BSON::ByteBuffer ] Buffer with serialized value.
437
444
  #
438
445
  # @since 2.5.0
439
- def self.serialize(buffer, value, validating_keys = BSON::Config.validating_keys?)
446
+ def self.serialize(buffer, value, validating_keys = nil)
440
447
  buffer.put_bytes(value)
441
448
  end
442
449
 
@@ -10,12 +10,9 @@ require 'mongo/protocol/caching_hash'
10
10
 
11
11
  # Client Requests
12
12
  require 'mongo/protocol/compressed'
13
- require 'mongo/protocol/delete'
14
13
  require 'mongo/protocol/get_more'
15
- require 'mongo/protocol/insert'
16
14
  require 'mongo/protocol/kill_cursors'
17
15
  require 'mongo/protocol/query'
18
- require 'mongo/protocol/update'
19
16
  require 'mongo/protocol/msg'
20
17
 
21
18
  # Server Responses
@@ -114,24 +114,24 @@ module Mongo
114
114
  #
115
115
  # @param [ Mongo::CachingCursor ] cursor The CachingCursor instance to store.
116
116
  #
117
- # @option opts [ String | nil ] namespace The namespace of the query,
117
+ # @option opts [ String | nil ] :namespace The namespace of the query,
118
118
  # in the format "database_name.collection_name".
119
- # @option opts [ Array, Hash ] selector The selector passed to the query.
119
+ # @option opts [ Array, Hash ] :selector The selector passed to the query.
120
120
  # For most queries, this will be a Hash, but for aggregations, this
121
121
  # will be an Array representing the aggregation pipeline. May not be nil.
122
- # @option opts [ Integer | nil ] skip The skip value of the query.
123
- # @option opts [ Hash | nil ] sort The order of the query results
122
+ # @option opts [ Integer | nil ] :skip The skip value of the query.
123
+ # @option opts [ Hash | nil ] :sort The order of the query results
124
124
  # (e.g. { name: -1 }).
125
- # @option opts [ Integer | nil ] limit The limit value of the query.
126
- # @option opts [ Hash | nil ] projection The projection of the query
125
+ # @option opts [ Integer | nil ] :limit The limit value of the query.
126
+ # @option opts [ Hash | nil ] :projection The projection of the query
127
127
  # results (e.g. { name: 1 }).
128
- # @option opts [ Hash | nil ] collation The collation of the query
128
+ # @option opts [ Hash | nil ] :collation The collation of the query
129
129
  # (e.g. { "locale" => "fr_CA" }).
130
- # @option opts [ Hash | nil ] read_concern The read concern of the query
130
+ # @option opts [ Hash | nil ] :read_concern The read concern of the query
131
131
  # (e.g. { level: :majority }).
132
- # @option opts [ Hash | nil ] read_preference The read preference of
132
+ # @option opts [ Hash | nil ] :read_preference The read preference of
133
133
  # the query (e.g. { mode: :secondary }).
134
- # @option opts [ Boolean | nil ] multi_collection Whether the query
134
+ # @option opts [ Boolean | nil ] :multi_collection Whether the query
135
135
  # results could potentially come from multiple collections. When true,
136
136
  # these results will be stored under the nil namespace key and cleared
137
137
  # on every write command.
@@ -152,24 +152,24 @@ module Mongo
152
152
  # For the given query options, retrieve a cached cursor that can be used
153
153
  # to obtain the correct query results, if one exists in the cache.
154
154
  #
155
- # @option opts [ String | nil ] namespace The namespace of the query,
155
+ # @option opts [ String | nil ] :namespace The namespace of the query,
156
156
  # in the format "database_name.collection_name".
157
- # @option opts [ Array, Hash ] selector The selector passed to the query.
157
+ # @option opts [ Array, Hash ] :selector The selector passed to the query.
158
158
  # For most queries, this will be a Hash, but for aggregations, this
159
159
  # will be an Array representing the aggregation pipeline. May not be nil.
160
- # @option opts [ Integer | nil ] skip The skip value of the query.
161
- # @option opts [ Hash | nil ] sort The order of the query results
160
+ # @option opts [ Integer | nil ] :skip The skip value of the query.
161
+ # @option opts [ Hash | nil ] :sort The order of the query results
162
162
  # (e.g. { name: -1 }).
163
- # @option opts [ Integer | nil ] limit The limit value of the query.
164
- # @option opts [ Hash | nil ] projection The projection of the query
163
+ # @option opts [ Integer | nil ] :limit The limit value of the query.
164
+ # @option opts [ Hash | nil ] :projection The projection of the query
165
165
  # results (e.g. { name: 1 }).
166
- # @option opts [ Hash | nil ] collation The collation of the query
166
+ # @option opts [ Hash | nil ] :collation The collation of the query
167
167
  # (e.g. { "locale" => "fr_CA" }).
168
- # @option opts [ Hash | nil ] read_concern The read concern of the query
168
+ # @option opts [ Hash | nil ] :read_concern The read concern of the query
169
169
  # (e.g. { level: :majority }).
170
- # @option opts [ Hash | nil ] read_preference The read preference of
170
+ # @option opts [ Hash | nil ] :read_preference The read preference of
171
171
  # the query (e.g. { mode: :secondary }).
172
- # @option opts [ Boolean | nil ] multi_collection Whether the query
172
+ # @option opts [ Boolean | nil ] :multi_collection Whether the query
173
173
  # results could potentially come from multiple collections. When true,
174
174
  # these results will be stored under the nil namespace key and cleared
175
175
  # on every write command.
@@ -0,0 +1,255 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2016-2023 MongoDB Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Mongo
18
+ class Server
19
+ class AppMetadata
20
+ # Implements the logic from the handshake spec, for deducing and
21
+ # reporting the current FaaS environment in which the program is
22
+ # executing.
23
+ #
24
+ # @api private
25
+ class Environment
26
+ # Error class for reporting that too many discriminators were found
27
+ # in the environment. (E.g. if the environment reports that it is
28
+ # running under both AWS and Azure.)
29
+ class TooManyEnvironments < Mongo::Error; end
30
+
31
+ # Error class for reporting that a required environment variable is
32
+ # missing.
33
+ class MissingVariable < Mongo::Error; end
34
+
35
+ # Error class for reporting that the wrong type was given for a
36
+ # field.
37
+ class TypeMismatch < Mongo::Error; end
38
+
39
+ # Error class for reporting that the value for a field is too long.
40
+ class ValueTooLong < Mongo::Error; end
41
+
42
+ # This value is not explicitly specified in the spec, only implied to be
43
+ # less than 512.
44
+ MAXIMUM_VALUE_LENGTH = 500
45
+
46
+ # The mapping that determines which FaaS environment is active, based
47
+ # on which environment variable(s) are present.
48
+ DISCRIMINATORS = {
49
+ 'AWS_EXECUTION_ENV' => { pattern: /^AWS_Lambda_/, name: 'aws.lambda' },
50
+ 'AWS_LAMBDA_RUNTIME_API' => { name: 'aws.lambda' },
51
+ 'FUNCTIONS_WORKER_RUNTIME' => { name: 'azure.func' },
52
+ 'K_SERVICE' => { name: 'gcp.func' },
53
+ 'FUNCTION_NAME' => { name: 'gcp.func' },
54
+ 'VERCEL' => { name: 'vercel' },
55
+ }.freeze
56
+
57
+ # Describes how to coerce values of the specified type.
58
+ COERCIONS = {
59
+ string: ->(v) { String(v) },
60
+ integer: ->(v) { Integer(v) }
61
+ }.freeze
62
+
63
+ # Describes which fields are required for each FaaS environment,
64
+ # along with their expected types, and how they should be named in
65
+ # the handshake document.
66
+ FIELDS = {
67
+ 'aws.lambda' => {
68
+ 'AWS_REGION' => { field: :region, type: :string },
69
+ 'AWS_LAMBDA_FUNCTION_MEMORY_SIZE' => { field: :memory_mb, type: :integer },
70
+ },
71
+
72
+ 'azure.func' => {},
73
+
74
+ 'gcp.func' => {
75
+ 'FUNCTION_MEMORY_MB' => { field: :memory_mb, type: :integer },
76
+ 'FUNCTION_TIMEOUT_SEC' => { field: :timeout_sec, type: :integer },
77
+ 'FUNCTION_REGION' => { field: :region, type: :string },
78
+ },
79
+
80
+ 'vercel' => {
81
+ 'VERCEL_URL' => { field: :url, type: :string },
82
+ 'VERCEL_REGION' => { field: :region, type: :string },
83
+ },
84
+ }.freeze
85
+
86
+ # @return [ String | nil ] the name of the FaaS environment that was
87
+ # detected, or nil if no valid FaaS environment was detected.
88
+ attr_reader :name
89
+
90
+ # @return [ Hash | nil ] the fields describing the detected FaaS
91
+ # environment.
92
+ attr_reader :fields
93
+
94
+ # @return [ String | nil ] the error message explaining why a valid
95
+ # FaaS environment was not detected, or nil if no error occurred.
96
+ #
97
+ # @note These error messagess are not to be propogated to the
98
+ # user; they are intended only for troubleshooting and debugging.)
99
+ attr_reader :error
100
+
101
+ # Create a new AppMetadata::Environment object, initializing it from
102
+ # the current ENV variables. If no FaaS environment is detected, or
103
+ # if the environment contains invalid or contradictory state, it will
104
+ # be initialized with {{name}} set to {{nil}}.
105
+ def initialize
106
+ @error = nil
107
+ @name = detect_environment
108
+ populate_fields
109
+ rescue TooManyEnvironments => e
110
+ self.error = "too many environments detected: #{e.message}"
111
+ rescue MissingVariable => e
112
+ self.error = "missing environment variable: #{e.message}"
113
+ rescue TypeMismatch => e
114
+ self.error = e.message
115
+ rescue ValueTooLong => e
116
+ self.error = "value for #{e.message} is too long"
117
+ end
118
+
119
+ # Queries whether the current environment is a valid FaaS environment.
120
+ #
121
+ # @return [ true | false ] whether the environment is a FaaS
122
+ # environment or not.
123
+ def faas?
124
+ @name != nil
125
+ end
126
+
127
+ # Queries whether the current environment is a valid AWS Lambda
128
+ # environment.
129
+ #
130
+ # @return [ true | false ] whether the environment is a AWS Lambda
131
+ # environment or not.
132
+ def aws?
133
+ @name == 'aws.lambda'
134
+ end
135
+
136
+ # Queries whether the current environment is a valid Azure
137
+ # environment.
138
+ #
139
+ # @return [ true | false ] whether the environment is a Azure
140
+ # environment or not.
141
+ def azure?
142
+ @name == 'azure.func'
143
+ end
144
+
145
+ # Queries whether the current environment is a valid GCP
146
+ # environment.
147
+ #
148
+ # @return [ true | false ] whether the environment is a GCP
149
+ # environment or not.
150
+ def gcp?
151
+ @name == 'gcp.func'
152
+ end
153
+
154
+ # Queries whether the current environment is a valid Vercel
155
+ # environment.
156
+ #
157
+ # @return [ true | false ] whether the environment is a Vercel
158
+ # environment or not.
159
+ def vercel?
160
+ @name == 'vercel'
161
+ end
162
+
163
+ # Compiles the detected environment information into a Hash. It will
164
+ # always include a {{name}} key, but may include other keys as well,
165
+ # depending on the detected FaaS environment. (See the handshake
166
+ # spec for details.)
167
+ #
168
+ # @return [ Hash ] the detected environment information.
169
+ def to_h
170
+ fields.merge(name: name)
171
+ end
172
+
173
+ private
174
+
175
+ # Searches the DESCRIMINATORS list to see which (if any) apply to
176
+ # the current environment.
177
+ #
178
+ # @return [ String | nil ] the name of the detected FaaS provider.
179
+ #
180
+ # @raise [ TooManyEnvironments ] if the environment contains
181
+ # discriminating variables for more than one FaaS provider.
182
+ def detect_environment
183
+ matches = DISCRIMINATORS.keys.select { |k| discriminator_matches?(k) }
184
+ names = matches.map { |m| DISCRIMINATORS[m][:name] }.uniq
185
+
186
+ raise TooManyEnvironments, names.join(', ') if names.length > 1
187
+
188
+ names.first
189
+ end
190
+
191
+ # Determines whether the named environment variable exists, and (if
192
+ # a pattern has been declared for that descriminator) whether the
193
+ # pattern matches the value of the variable.
194
+ #
195
+ # @param [ String ] var the name of the environment variable
196
+ #
197
+ # @return [ true | false ] if the variable describes the current
198
+ # environment or not.
199
+ def discriminator_matches?(var)
200
+ return false unless ENV[var]
201
+
202
+ disc = DISCRIMINATORS[var]
203
+ return true unless disc[:pattern]
204
+
205
+ disc[:pattern].match?(ENV[var])
206
+ end
207
+
208
+ # Extracts environment information from the current environment
209
+ # variables, based on the detected FaaS environment. Populates the
210
+ # {{@fields}} instance variable.
211
+ def populate_fields
212
+ return unless name
213
+
214
+ @fields = FIELDS[name].each_with_object({}) do |(var, defn), fields|
215
+ fields[defn[:field]] = extract_field(var, defn)
216
+ end
217
+ end
218
+
219
+ # Extracts the named variable from the environment and validates it
220
+ # against its declared definition.
221
+ #
222
+ # @param [ String ] var The name of the environment variable to look
223
+ # for.
224
+ # @param [ Hash ] definition The definition of the field that applies
225
+ # to the named variable.
226
+ #
227
+ # @return [ Integer | String ] the validated and coerced value of the
228
+ # given environment variable.
229
+ #
230
+ # @raise [ MissingVariable ] if the environment does not include a
231
+ # variable required by the current FaaS provider.
232
+ # @raise [ ValueTooLong ] if a required variable is too long.
233
+ # @raise [ TypeMismatch ] if a required variable cannot be coerced to
234
+ # the expected type.
235
+ def extract_field(var, definition)
236
+ raise MissingVariable, var unless ENV[var]
237
+ raise ValueTooLong, var if ENV[var].length > MAXIMUM_VALUE_LENGTH
238
+
239
+ COERCIONS[definition[:type]].call(ENV[var])
240
+ rescue ArgumentError
241
+ raise TypeMismatch,
242
+ "#{var} must be #{definition[:type]} (got #{ENV[var].inspect})"
243
+ end
244
+
245
+ # Sets the error message to the given value and sets the name to nil.
246
+ #
247
+ # @param [ String ] msg The error message to store.
248
+ def error=(msg)
249
+ @name = nil
250
+ @error = msg
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2016-2023 MongoDB Inc.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Mongo
18
+ class Server
19
+ class AppMetadata
20
+ # Implements the metadata truncation logic described in the handshake
21
+ # spec.
22
+ #
23
+ # @api private
24
+ class Truncator
25
+ # @return [ BSON::Document ] the document being truncated.
26
+ attr_reader :document
27
+
28
+ # The max application metadata document byte size.
29
+ MAX_DOCUMENT_SIZE = 512
30
+
31
+ # Creates a new Truncator instance and tries enforcing the maximum
32
+ # document size on the given document.
33
+ #
34
+ # @param [ BSON::Document] document The document to (potentially)
35
+ # truncate.
36
+ #
37
+ # @note The document is modified in-place; if you wish to keep the
38
+ # original unchanged, you must deep-clone it prior to sending it to
39
+ # a truncator.
40
+ def initialize(document)
41
+ @document = document
42
+ try_truncate!
43
+ end
44
+
45
+ # The current size of the document, in bytes, as a serialized BSON
46
+ # document.
47
+ #
48
+ # @return [ Integer ] the size of the document
49
+ def size
50
+ @document.to_bson.to_s.length
51
+ end
52
+
53
+ # Whether the document fits within the required maximum document size.
54
+ #
55
+ # @return [ true | false ] if the document is okay or not.
56
+ def ok?
57
+ size <= MAX_DOCUMENT_SIZE
58
+ end
59
+
60
+ private
61
+
62
+ # How many extra bytes must be trimmed before the document may be
63
+ # considered #ok?.
64
+ #
65
+ # @return [ Integer ] how many bytes larger the document is than the
66
+ # maximum document size.
67
+ def excess
68
+ size - MAX_DOCUMENT_SIZE
69
+ end
70
+
71
+ # Attempt to truncate the document using the documented metadata
72
+ # priorities (see the handshake specification).
73
+ def try_truncate!
74
+ %i[ env_fields os_fields env platform ].each do |target|
75
+ break if ok?
76
+
77
+ send(:"try_truncate_#{target}!")
78
+ end
79
+ end
80
+
81
+ # Attempt to truncate or remove the {{:platform}} key from the
82
+ # document.
83
+ def try_truncate_platform!
84
+ @document.delete(:platform) unless try_truncate_string(@document[:platform])
85
+ end
86
+
87
+ # Attempt to truncate the keys in the {{:env}} subdocument.
88
+ def try_truncate_env_fields!
89
+ try_truncate_hash(@document[:env], reserved: %w[ name ])
90
+ end
91
+
92
+ # Attempt to truncate the keys in the {{:os}} subdocument.
93
+ def try_truncate_os_fields!
94
+ try_truncate_hash(@document[:os], reserved: %w[ type ])
95
+ end
96
+
97
+ # Remove the {{:env}} key from the document.
98
+ def try_truncate_env!
99
+ @document.delete(:env)
100
+ end
101
+
102
+ # A helper method for truncating a string (in-place) by whatever
103
+ # {{#excess}} is required.
104
+ #
105
+ # @param [ String ] string the string value to truncate.
106
+ #
107
+ # @note the parameter is modified in-place.
108
+ def try_truncate_string(string)
109
+ length = string&.length || 0
110
+
111
+ return false if excess > length
112
+
113
+ string[(length - excess)..-1] = ''
114
+ end
115
+
116
+ # A helper method for removing the keys of a Hash (in-place) until
117
+ # the document is the necessary size. The keys are considered in order
118
+ # (using the Hash's native key ordering), and each will be removed from
119
+ # the hash in turn, until the document is the necessary size.
120
+ #
121
+ # Any keys in the {{reserved}} list will be ignored.
122
+ #
123
+ # @param [ Hash | nil ] hash the Hash instance to consider.
124
+ # @param [ Array ] reserved the list of keys to ignore in the hash.
125
+ #
126
+ # @note the hash parameter is modified in-place.
127
+ def try_truncate_hash(hash, reserved: [])
128
+ return false unless hash
129
+
130
+ keys = hash.keys - reserved
131
+ keys.each do |key|
132
+ hash.delete(key)
133
+
134
+ return true if ok?
135
+ end
136
+
137
+ false
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end