@@ -18,9 +18,12 @@ module Mongo
18
18
class Server
19
19
class AppMetadata
20
20
# Implements the logic from the handshake spec, for deducing and
21
- # reporting the current FaaS environment in which the program is
21
+ # reporting the current environment in which the program is
22
22
# executing.
23
23
#
24
+ # This includes FaaS environment checks, as well as checks for the
25
+ # presence of a container (Docker) and/or orchestrator (Kubernetes).
26
+ #
24
27
# @api private
25
28
class Environment
26
29
# Error class for reporting that too many discriminators were found
@@ -39,6 +42,10 @@ class TypeMismatch < Mongo::Error; end
39
42
# Error class for reporting that the value for a field is too long.
40
43
class ValueTooLong < Mongo ::Error ; end
41
44
45
+ # The name and location of the .dockerenv file that will signal the
46
+ # presence of Docker.
47
+ DOCKERENV_PATH = '/.dockerenv'
48
+
42
49
# This value is not explicitly specified in the spec, only implied to be
43
50
# less than 512.
44
51
MAXIMUM_VALUE_LENGTH = 500
@@ -102,9 +109,11 @@ class ValueTooLong < Mongo::Error; end
102
109
# if the environment contains invalid or contradictory state, it will
103
110
# be initialized with {{name}} set to {{nil}}.
104
111
def initialize
112
+ @fields = { }
105
113
@error = nil
106
114
@name = detect_environment
107
- populate_fields
115
+ populate_faas_fields
116
+ detect_container
108
117
rescue TooManyEnvironments => e
109
118
self . error = "too many environments detected: #{ e . message } "
110
119
rescue MissingVariable => e
@@ -115,6 +124,23 @@ def initialize
115
124
self . error = "value for #{ e . message } is too long"
116
125
end
117
126
127
+ # Queries the detected container information.
128
+ #
129
+ # @return [ Hash | nil ] the detected container information, or
130
+ # nil if no container was detected.
131
+ def container
132
+ fields [ :container ]
133
+ end
134
+
135
+ # Queries whether any environment information was able to be
136
+ # detected.
137
+ #
138
+ # @return [ true | false ] if any environment information was
139
+ # detected.
140
+ def present?
141
+ @name || fields . any?
142
+ end
143
+
118
144
# Queries whether the current environment is a valid FaaS environment.
119
145
#
120
146
# @return [ true | false ] whether the environment is a FaaS
@@ -159,14 +185,11 @@ def vercel?
159
185
@name == 'vercel'
160
186
end
161
187
162
- # Compiles the detected environment information into a Hash. It will
163
- # always include a {{name}} key, but may include other keys as well,
164
- # depending on the detected FaaS environment. (See the handshake
165
- # spec for details.)
188
+ # Compiles the detected environment information into a Hash.
166
189
#
167
190
# @return [ Hash ] the detected environment information.
168
191
def to_h
169
- fields . merge ( name : name )
192
+ name ? fields . merge ( name : name ) : fields
170
193
end
171
194
172
195
private
@@ -192,6 +215,38 @@ def detect_environment
192
215
names . first
193
216
end
194
217
218
+ # Looks for the presence of a container. Currently can detect
219
+ # Docker (by the existence of a .dockerenv file in the root
220
+ # directory) and Kubernetes (by the existence of the KUBERNETES_SERVICE_HOST
221
+ # environment variable).
222
+ def detect_container
223
+ runtime = docker_present? && 'docker'
224
+ orchestrator = kubernetes_present? && 'kubernetes'
225
+
226
+ return unless runtime || orchestrator
227
+
228
+ fields [ :container ] = { }
229
+ fields [ :container ] [ :runtime ] = runtime if runtime
230
+ fields [ :container ] [ :orchestrator ] = orchestrator if orchestrator
231
+ end
232
+
233
+ # Checks for the existence of a .dockerenv in the root directory.
234
+ def docker_present?
235
+ File . exist? ( dockerenv_path )
236
+ end
237
+
238
+ # Implementing this as a method so that it can be mocked in tests, to
239
+ # test the presence or absence of Docker.
240
+ def dockerenv_path
241
+ DOCKERENV_PATH
242
+ end
243
+
244
+ # Checks for the presence of a non-empty KUBERNETES_SERVICE_HOST
245
+ # environment variable.
246
+ def kubernetes_present?
247
+ !ENV [ 'KUBERNETES_SERVICE_HOST' ] . to_s . empty?
248
+ end
249
+
195
250
# Determines whether the named environment variable exists, and (if
196
251
# a pattern has been declared for that descriminator) whether the
197
252
# pattern matches the value of the variable.
@@ -212,10 +267,10 @@ def discriminator_matches?(var)
212
267
# Extracts environment information from the current environment
213
268
# variables, based on the detected FaaS environment. Populates the
214
269
# {{@fields}} instance variable.
215
- def populate_fields
270
+ def populate_faas_fields
216
271
return unless name
217
272
218
- @fields = FIELDS [ name ] . each_with_object ( { } ) do |( var , defn ) , fields |
273
+ FIELDS [ name ] . each_with_object ( @fields ) do |( var , defn ) , fields |
219
274
fields [ defn [ :field ] ] = extract_field ( var , defn )
220
275
end
221
276
end
0 commit comments