mongo 2.18.2 → 2.18.3
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/README.md +1 -1
- data/lib/mongo/collection/view/iterable.rb +15 -0
- data/lib/mongo/collection/view.rb +1 -0
- data/lib/mongo/crypt/auto_encrypter.rb +1 -0
- data/lib/mongo/crypt/explicit_encrypter.rb +1 -0
- data/lib/mongo/crypt.rb +11 -0
- data/lib/mongo/grid/file/chunk.rb +2 -1
- data/lib/mongo/grid/file/info.rb +2 -1
- data/lib/mongo/protocol/bit_vector.rb +3 -1
- data/lib/mongo/protocol/caching_hash.rb +3 -20
- data/lib/mongo/protocol/message.rb +4 -8
- data/lib/mongo/protocol/msg.rb +1 -0
- data/lib/mongo/protocol/serializers.rb +24 -17
- data/lib/mongo/server/app_metadata/environment.rb +255 -0
- data/lib/mongo/server/app_metadata/truncator.rb +142 -0
- data/lib/mongo/server/app_metadata.rb +29 -42
- data/lib/mongo/version.rb +1 -1
- data/spec/integration/connection/faas_env_spec.rb +63 -0
- data/spec/integration/find_options_spec.rb +227 -0
- data/spec/integration/ocsp_verifier_spec.rb +1 -1
- data/spec/lite_spec_helper.rb +9 -0
- data/spec/mongo/address_spec.rb +1 -1
- data/spec/mongo/client_construction_spec.rb +3 -3
- data/spec/mongo/client_spec.rb +1 -9
- data/spec/mongo/cluster_spec.rb +2 -2
- data/spec/mongo/crypt_spec.rb +21 -0
- data/spec/mongo/index/view_spec.rb +1 -0
- data/spec/mongo/protocol/caching_hash_spec.rb +0 -45
- data/spec/mongo/protocol/msg_spec.rb +2 -4
- data/spec/mongo/server/app_metadata/environment_spec.rb +193 -0
- data/spec/mongo/server/app_metadata/truncator_spec.rb +158 -0
- data/spec/mongo/server/app_metadata_spec.rb +33 -47
- data/spec/mongo/socket/ssl_spec.rb +2 -8
- data/spec/runners/crud/requirement.rb +2 -2
- data/spec/shared/lib/mrss/docker_runner.rb +4 -0
- data/spec/shared/lib/mrss/lite_constraints.rb +8 -0
- data/spec/shared/lib/mrss/server_version_registry.rb +16 -23
- data/spec/shared/share/Dockerfile.erb +24 -19
- data/spec/shared/shlib/server.sh +31 -7
- data/spec/shared/shlib/set_env.sh +4 -4
- data/spec/solo/clean_exit_spec.rb +3 -10
- data/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml +15 -6
- data/spec/spec_tests/data/command_monitoring_unified/redacted-commands.yml +8 -0
- data/spec/support/aws_utils.rb +3 -3
- data/spec/support/certificates/atlas-ocsp-ca.crt +67 -67
- data/spec/support/certificates/atlas-ocsp.crt +103 -103
- data/spec/support/shared/app_metadata.rb +14 -2
- data.tar.gz.sig +0 -0
- metadata +1155 -1138
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c36a5a34e34ad90dc310f1151d98257f36c6c2bbea07282963f631f06a450eb
|
4
|
+
data.tar.gz: 220b2abd528a4f540d08cf08e78e256be845ae5b9bcf41f52f92ce9c98e554b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8fd8cdeee661a65f74b3825db234492357bb82f3b4ec595f0df9477e0b03f62ac093c33d31a0ff7b766868556662b32ee74c3ff87be379cda4bc5363e2e37b0e
|
7
|
+
data.tar.gz: 2d202097305e6b8fa1fba5541fe8d6d630f1beb96cef753294d16ccfbaa1d114547208868d16fc3b48a9e5b897144edff580bcb8edab28123a089f32ded1340e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/README.md
CHANGED
@@ -185,6 +185,8 @@ module Mongo
|
|
185
185
|
collection.client.log_warn("The :oplog_replay option is deprecated and ignored by MongoDB 4.4 and later")
|
186
186
|
end
|
187
187
|
|
188
|
+
maybe_set_tailable_options(spec)
|
189
|
+
|
188
190
|
if explained?
|
189
191
|
spec[:explain] = options[:explain]
|
190
192
|
Operation::Explain.new(spec)
|
@@ -200,6 +202,19 @@ module Mongo
|
|
200
202
|
def use_query_cache?
|
201
203
|
QueryCache.enabled? && !collection.system_collection?
|
202
204
|
end
|
205
|
+
|
206
|
+
# Add tailable cusror options to the command specifiction if needed.
|
207
|
+
#
|
208
|
+
# @param [ Hash ] spec The command specification.
|
209
|
+
def maybe_set_tailable_options(spec)
|
210
|
+
case cursor_type
|
211
|
+
when :tailable
|
212
|
+
spec[:tailable] = true
|
213
|
+
when :tailable_await
|
214
|
+
spec[:tailable] = true
|
215
|
+
spec[:await_data] = true
|
216
|
+
end
|
217
|
+
end
|
203
218
|
end
|
204
219
|
end
|
205
220
|
end
|
@@ -127,6 +127,7 @@ module Mongo
|
|
127
127
|
# return in each response from MongoDB.
|
128
128
|
# @option options [ Hash ] :collation The collation to use.
|
129
129
|
# @option options [ String ] :comment Associate a comment with the query.
|
130
|
+
# @option options [ :tailable, :tailable_await ] :cursor_type The type of cursor to use.
|
130
131
|
# @option options [ Hash ] :explain Execute an explain with the provided
|
131
132
|
# explain options (known options are :verbose and :verbosity) rather
|
132
133
|
# than a find.
|
@@ -91,6 +91,7 @@ module Mongo
|
|
91
91
|
# @raise [ ArgumentError ] If required options are missing or incorrectly
|
92
92
|
# formatted.
|
93
93
|
def initialize(options)
|
94
|
+
Crypt.validate_ffi!
|
94
95
|
# Note that this call may eventually, via other method invocations,
|
95
96
|
# create additional clients which have to be cleaned up.
|
96
97
|
@options = set_default_options(options).freeze
|
@@ -36,6 +36,7 @@ module Mongo
|
|
36
36
|
# should be hashes of TLS connection options. The options are equivalent
|
37
37
|
# to TLS connection options of Mongo::Client.
|
38
38
|
def initialize(key_vault_client, key_vault_namespace, kms_providers, kms_tls_options)
|
39
|
+
Crypt.validate_ffi!
|
39
40
|
@crypt_handle = Handle.new(
|
40
41
|
kms_providers,
|
41
42
|
kms_tls_options,
|
data/lib/mongo/crypt.rb
CHANGED
@@ -35,5 +35,16 @@ module Mongo
|
|
35
35
|
autoload(:ExplicitEncrypter, 'mongo/crypt/explicit_encrypter')
|
36
36
|
autoload(:AutoEncrypter, 'mongo/crypt/auto_encrypter')
|
37
37
|
autoload(:KMS, 'mongo/crypt/kms')
|
38
|
+
|
39
|
+
def validate_ffi!
|
40
|
+
return if defined?(FFI)
|
41
|
+
|
42
|
+
require 'ffi'
|
43
|
+
rescue LoadError => e
|
44
|
+
raise Error::UnmetDependency, 'Cannot enable encryption because the ffi gem ' \
|
45
|
+
"has not been installed. Add \"gem 'ffi'\" to your Gemfile and run " \
|
46
|
+
"\"bundle install\" to install the gem. (#{e.class}: #{e})"
|
47
|
+
end
|
48
|
+
module_function :validate_ffi!
|
38
49
|
end
|
39
50
|
end
|
@@ -133,11 +133,12 @@ module Mongo
|
|
133
133
|
#
|
134
134
|
# @param [ BSON::ByteBuffer ] buffer The encoded BSON buffer to append to.
|
135
135
|
# @param [ true, false ] validating_keys Whether keys should be validated when serializing.
|
136
|
+
# This option is deprecated and will not be used. It will removed in version 3.0.
|
136
137
|
#
|
137
138
|
# @return [ String ] The raw BSON data.
|
138
139
|
#
|
139
140
|
# @since 2.0.0
|
140
|
-
def to_bson(buffer = BSON::ByteBuffer.new, validating_keys =
|
141
|
+
def to_bson(buffer = BSON::ByteBuffer.new, validating_keys = nil)
|
141
142
|
document.to_bson(buffer)
|
142
143
|
end
|
143
144
|
|
data/lib/mongo/grid/file/info.rb
CHANGED
@@ -228,11 +228,12 @@ module Mongo
|
|
228
228
|
#
|
229
229
|
# @param [ BSON::ByteBuffer ] buffer The encoded BSON buffer to append to.
|
230
230
|
# @param [ true, false ] validating_keys Whether keys should be validated when serializing.
|
231
|
+
# This option is deprecated and will not be used. It will removed in version 3.0.
|
231
232
|
#
|
232
233
|
# @return [ String ] The raw BSON data.
|
233
234
|
#
|
234
235
|
# @since 2.0.0
|
235
|
-
def to_bson(buffer = BSON::ByteBuffer.new, validating_keys =
|
236
|
+
def to_bson(buffer = BSON::ByteBuffer.new, validating_keys = nil)
|
236
237
|
if @client_md5 && !document[:md5]
|
237
238
|
document[:md5] = @client_md5.hexdigest
|
238
239
|
end
|
@@ -39,9 +39,11 @@ module Mongo
|
|
39
39
|
#
|
40
40
|
# @param buffer [ String ] Buffer to receive the serialized vector
|
41
41
|
# @param value [ Array<Symbol> ] Array of flags to encode
|
42
|
+
# @param [ true, false ] validating_keys Whether keys should be validated when serializing.
|
43
|
+
# This option is deprecated and will not be used. It will removed in version 3.0.
|
42
44
|
#
|
43
45
|
# @return [ String ] Buffer that received the serialized vector
|
44
|
-
def serialize(buffer, value, validating_keys =
|
46
|
+
def serialize(buffer, value, validating_keys = nil)
|
45
47
|
bits = 0
|
46
48
|
value.each { |flag| bits |= (@masks[flag] || 0) }
|
47
49
|
buffer.put_int32(bits)
|
@@ -38,32 +38,15 @@ module Mongo
|
|
38
38
|
#
|
39
39
|
# @param [ BSON::ByteBuffer ] buffer The encoded BSON buffer to append to.
|
40
40
|
# @param [ true, false ] validating_keys Whether keys should be validated when serializing.
|
41
|
+
# This option is deprecated and will not be used. It will removed in version 3.0.
|
41
42
|
#
|
42
43
|
# @return [ BSON::ByteBuffer ] The buffer with the encoded object.
|
43
|
-
def to_bson(buffer = BSON::ByteBuffer.new, validating_keys =
|
44
|
+
def to_bson(buffer = BSON::ByteBuffer.new, validating_keys = nil)
|
44
45
|
if !@bytes
|
45
|
-
@bytes = @hash.to_bson(BSON::ByteBuffer.new
|
46
|
-
elsif needs_validation?(validating_keys)
|
47
|
-
@validated = true
|
48
|
-
return @hash.to_bson(buffer, validating_keys)
|
46
|
+
@bytes = @hash.to_bson(BSON::ByteBuffer.new).to_s
|
49
47
|
end
|
50
|
-
@validated ||= validating_keys
|
51
48
|
buffer.put_bytes(@bytes)
|
52
49
|
end
|
53
|
-
|
54
|
-
private
|
55
|
-
|
56
|
-
# Checks the current value for validating keys, and whether or not this
|
57
|
-
# bson has been validated in the past, and decides if we need to recalculate
|
58
|
-
# the to_bson to check the validations.
|
59
|
-
#
|
60
|
-
# @param [ true, false ] validating_keys Whether keys should be validated when serializing.
|
61
|
-
#
|
62
|
-
# @return [ true, false ] Whether or not the bson needs to be recalculated
|
63
|
-
# with validation.
|
64
|
-
def needs_validation?(validating_keys)
|
65
|
-
!@validated && validating_keys
|
66
|
-
end
|
67
50
|
end
|
68
51
|
end
|
69
52
|
end
|
@@ -354,16 +354,16 @@ module Mongo
|
|
354
354
|
if field[:multi]
|
355
355
|
value.each do |item|
|
356
356
|
if field[:type].respond_to?(:size_limited?)
|
357
|
-
field[:type].serialize(buffer, item, max_bson_size
|
357
|
+
field[:type].serialize(buffer, item, max_bson_size)
|
358
358
|
else
|
359
|
-
field[:type].serialize(buffer, item
|
359
|
+
field[:type].serialize(buffer, item)
|
360
360
|
end
|
361
361
|
end
|
362
362
|
else
|
363
363
|
if field[:type].respond_to?(:size_limited?)
|
364
|
-
field[:type].serialize(buffer, value, max_bson_size
|
364
|
+
field[:type].serialize(buffer, value, max_bson_size)
|
365
365
|
else
|
366
|
-
field[:type].serialize(buffer, value
|
366
|
+
field[:type].serialize(buffer, value)
|
367
367
|
end
|
368
368
|
end
|
369
369
|
end
|
@@ -456,10 +456,6 @@ module Mongo
|
|
456
456
|
field[:type].deserialize(io, options)
|
457
457
|
)
|
458
458
|
end
|
459
|
-
|
460
|
-
def validating_keys?
|
461
|
-
@options[:validating_keys] if @options
|
462
|
-
end
|
463
459
|
end
|
464
460
|
end
|
465
461
|
end
|
data/lib/mongo/protocol/msg.rb
CHANGED
@@ -58,6 +58,7 @@ module Mongo
|
|
58
58
|
# @option options [ true, false ] validating_keys Whether keys should be
|
59
59
|
# validated for being valid document keys (i.e. not begin with $ and
|
60
60
|
# not contain dots).
|
61
|
+
# This option is deprecated and will not be used. It will removed in version 3.0.
|
61
62
|
#
|
62
63
|
# @api private
|
63
64
|
#
|
@@ -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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
196
|
+
PayloadZero.serialize(buffer, section[:payload], max_bson_size)
|
194
197
|
when nil
|
195
|
-
PayloadZero.serialize(buffer, section[:payload], max_bson_size
|
198
|
+
PayloadZero.serialize(buffer, section[:payload], max_bson_size)
|
196
199
|
when PayloadOne::TYPE
|
197
|
-
PayloadOne.serialize(buffer, section[:payload], max_bson_size
|
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 =
|
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
|
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 =
|
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
|
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 =
|
364
|
+
def self.serialize(buffer, value, max_bson_size = nil, validating_keys = nil)
|
360
365
|
start_size = buffer.length
|
361
|
-
value.to_bson(buffer
|
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 =
|
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 =
|
446
|
+
def self.serialize(buffer, value, validating_keys = nil)
|
440
447
|
buffer.put_bytes(value)
|
441
448
|
end
|
442
449
|
|
@@ -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
|