Skip to content

Commit ca7bf1d

Browse files
authored
RUST-1501 Collect FaaS platform metadata (#844)
1 parent 20a071a commit ca7bf1d

File tree

3 files changed

+338
-4
lines changed

3 files changed

+338
-4
lines changed

src/cmap/establish/handshake/mod.rs

Lines changed: 221 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#[cfg(test)]
22
mod test;
33

4+
use std::env;
5+
46
use lazy_static::lazy_static;
57

68
use crate::{
@@ -32,6 +34,7 @@ struct ClientMetadata {
3234
driver: DriverMetadata,
3335
os: OsMetadata,
3436
platform: String,
37+
env: Option<FaasEnvironment>,
3538
}
3639

3740
#[derive(Clone, Debug)]
@@ -49,10 +52,28 @@ struct DriverMetadata {
4952
struct OsMetadata {
5053
os_type: String,
5154
name: Option<String>,
52-
architecture: String,
55+
architecture: Option<String>,
5356
version: Option<String>,
5457
}
5558

59+
#[derive(Clone, Debug)]
60+
struct FaasEnvironment {
61+
name: FaasEnvironmentName,
62+
runtime: Option<String>,
63+
timeout_sec: Option<i32>,
64+
memory_mb: Option<i32>,
65+
region: Option<String>,
66+
url: Option<String>,
67+
}
68+
69+
#[derive(Copy, Clone, Debug)]
70+
enum FaasEnvironmentName {
71+
AwsLambda,
72+
AzureFunc,
73+
GcpFunc,
74+
Vercel,
75+
}
76+
5677
impl From<ClientMetadata> for Bson {
5778
fn from(metadata: ClientMetadata) -> Self {
5879
let mut metadata_doc = Document::new();
@@ -72,6 +93,10 @@ impl From<ClientMetadata> for Bson {
7293
metadata_doc.insert("os", metadata.os);
7394
metadata_doc.insert("platform", metadata.platform);
7495

96+
if let Some(env) = metadata.env {
97+
metadata_doc.insert("env", env);
98+
}
99+
75100
Bson::Document(metadata_doc)
76101
}
77102
}
@@ -84,7 +109,9 @@ impl From<OsMetadata> for Bson {
84109
doc.insert("name", name);
85110
}
86111

87-
doc.insert("architecture", metadata.architecture);
112+
if let Some(arch) = metadata.architecture {
113+
doc.insert("architecture", arch);
114+
}
88115

89116
if let Some(version) = metadata.version {
90117
doc.insert("version", version);
@@ -94,6 +121,141 @@ impl From<OsMetadata> for Bson {
94121
}
95122
}
96123

124+
impl From<FaasEnvironment> for Bson {
125+
fn from(env: FaasEnvironment) -> Self {
126+
let FaasEnvironment {
127+
name,
128+
runtime,
129+
timeout_sec,
130+
memory_mb,
131+
region,
132+
url,
133+
} = env;
134+
let mut out = doc! {
135+
"name": name.name(),
136+
};
137+
if let Some(rt) = runtime {
138+
out.insert("runtime", rt);
139+
}
140+
if let Some(t) = timeout_sec {
141+
out.insert("timeout_sec", t);
142+
}
143+
if let Some(m) = memory_mb {
144+
out.insert("memory_mb", m);
145+
}
146+
if let Some(r) = region {
147+
out.insert("region", r);
148+
}
149+
if let Some(u) = url {
150+
out.insert("url", u);
151+
}
152+
Bson::Document(out)
153+
}
154+
}
155+
156+
impl FaasEnvironment {
157+
const UNSET: Self = FaasEnvironment {
158+
name: FaasEnvironmentName::AwsLambda,
159+
runtime: None,
160+
timeout_sec: None,
161+
memory_mb: None,
162+
region: None,
163+
url: None,
164+
};
165+
166+
fn new() -> Option<Self> {
167+
let name = FaasEnvironmentName::new()?;
168+
Some(match name {
169+
FaasEnvironmentName::AwsLambda => {
170+
let runtime = env::var("AWS_EXECUTION_ENV").ok();
171+
let region = env::var("AWS_REGION").ok();
172+
let memory_mb = env::var("AWS_LAMBDA_FUNCTION_MEMORY_SIZE")
173+
.ok()
174+
.and_then(|s| s.parse().ok());
175+
Self {
176+
name,
177+
runtime,
178+
region,
179+
memory_mb,
180+
..Self::UNSET
181+
}
182+
}
183+
FaasEnvironmentName::AzureFunc => {
184+
let runtime = env::var("FUNCTIONS_WORKER_RUNTIME").ok();
185+
Self {
186+
name,
187+
runtime,
188+
..Self::UNSET
189+
}
190+
}
191+
FaasEnvironmentName::GcpFunc => {
192+
let memory_mb = env::var("FUNCTION_MEMORY_MB")
193+
.ok()
194+
.and_then(|s| s.parse().ok());
195+
let timeout_sec = env::var("FUNCTION_TIMEOUT_SEC")
196+
.ok()
197+
.and_then(|s| s.parse().ok());
198+
let region = env::var("FUNCTION_REGION").ok();
199+
Self {
200+
name,
201+
memory_mb,
202+
timeout_sec,
203+
region,
204+
..Self::UNSET
205+
}
206+
}
207+
FaasEnvironmentName::Vercel => {
208+
let url = env::var("VERCEL_URL").ok();
209+
let region = env::var("VERCEL_REGION").ok();
210+
Self {
211+
name,
212+
url,
213+
region,
214+
..Self::UNSET
215+
}
216+
}
217+
})
218+
}
219+
}
220+
221+
fn var_set(name: &str) -> bool {
222+
env::var_os(name).map_or(false, |v| !v.is_empty())
223+
}
224+
225+
impl FaasEnvironmentName {
226+
fn new() -> Option<Self> {
227+
use FaasEnvironmentName::*;
228+
let mut found = vec![];
229+
if var_set("AWS_EXECUTION_ENV") || var_set("AWS_LAMBDA_RUNTIME_API") {
230+
found.push(AwsLambda);
231+
}
232+
if var_set("FUNCTIONS_WORKER_RUNTIME") {
233+
found.push(AzureFunc);
234+
}
235+
if var_set("K_SERVICE") || var_set("FUNCTION_NAME") {
236+
found.push(GcpFunc);
237+
}
238+
if var_set("VERCEL") {
239+
found.push(Vercel);
240+
}
241+
if found.len() != 1 {
242+
None
243+
} else {
244+
Some(found[0])
245+
}
246+
}
247+
248+
fn name(&self) -> &'static str {
249+
use FaasEnvironmentName::*;
250+
match self {
251+
AwsLambda => "aws.lambda",
252+
AzureFunc => "azure.func",
253+
GcpFunc => "gcp.func",
254+
Vercel => "vercel",
255+
}
256+
}
257+
}
258+
97259
lazy_static! {
98260
/// Contains the basic handshake information that can be statically determined. This document
99261
/// (potentially with additional fields added) can be cloned and put in the `client` field of
@@ -107,15 +269,47 @@ lazy_static! {
107269
},
108270
os: OsMetadata {
109271
os_type: std::env::consts::OS.into(),
110-
architecture: std::env::consts::ARCH.into(),
272+
architecture: Some(std::env::consts::ARCH.into()),
111273
name: None,
112274
version: None,
113275
},
114276
platform: format!("{} with {}", rustc_version_runtime::version_meta().short_version_string, RUNTIME_NAME),
277+
env: None,
115278
}
116279
};
117280
}
118281

282+
type Truncation = fn(&mut ClientMetadata);
283+
284+
const METADATA_TRUNCATIONS: &[Truncation] = &[
285+
// truncate `platform`
286+
|metadata| {
287+
metadata.platform = rustc_version_runtime::version_meta().short_version_string;
288+
},
289+
// clear `env.*` except `name`
290+
|metadata| {
291+
if let Some(env) = &mut metadata.env {
292+
*env = FaasEnvironment {
293+
name: env.name,
294+
..FaasEnvironment::UNSET
295+
}
296+
}
297+
},
298+
// clear `os.*` except `type`
299+
|metadata| {
300+
metadata.os = OsMetadata {
301+
os_type: metadata.os.os_type.clone(),
302+
architecture: None,
303+
name: None,
304+
version: None,
305+
}
306+
},
307+
// clear `env`
308+
|metadata| {
309+
metadata.env = None;
310+
},
311+
];
312+
119313
/// Contains the logic needed to handshake a connection.
120314
#[derive(Clone, Debug)]
121315
pub(crate) struct Handshaker {
@@ -130,6 +324,8 @@ pub(crate) struct Handshaker {
130324
http_client: HttpClient,
131325

132326
server_api: Option<ServerApi>,
327+
328+
metadata: ClientMetadata,
133329
}
134330

135331
impl Handshaker {
@@ -164,6 +360,8 @@ impl Handshaker {
164360
}
165361
}
166362

363+
metadata.env = FaasEnvironment::new();
364+
167365
if options.load_balanced {
168366
command.body.insert("loadBalanced", true);
169367
}
@@ -180,13 +378,14 @@ impl Handshaker {
180378
);
181379
}
182380

183-
command.body.insert("client", metadata);
381+
command.body.insert("client", metadata.clone());
184382

185383
Self {
186384
http_client,
187385
command,
188386
compressors,
189387
server_api: options.server_api,
388+
metadata,
190389
}
191390
}
192391

@@ -205,6 +404,16 @@ impl Handshaker {
205404

206405
let client_first = set_speculative_auth_info(&mut command.body, credential)?;
207406

407+
let body = &mut command.body;
408+
let mut trunc_meta = self.metadata.clone();
409+
for trunc_fn in METADATA_TRUNCATIONS {
410+
if doc_size(body)? <= MAX_HELLO_SIZE {
411+
break;
412+
}
413+
trunc_fn(&mut trunc_meta);
414+
body.insert("client", trunc_meta.clone());
415+
}
416+
208417
let mut hello_reply = run_hello(conn, command).await?;
209418

210419
conn.stream_description = Some(StreamDescription::from_hello_reply(&hello_reply));
@@ -308,3 +517,11 @@ fn set_speculative_auth_info(
308517

309518
Ok(Some(client_first))
310519
}
520+
521+
fn doc_size(d: &Document) -> Result<usize> {
522+
let mut tmp = vec![];
523+
d.to_writer(&mut tmp)?;
524+
Ok(tmp.len())
525+
}
526+
527+
const MAX_HELLO_SIZE: usize = 512;

0 commit comments

Comments
 (0)