1use regex::Regex;
5use std::env;
6use std::sync::LazyLock;
7
8const WEBSITE_OWNER_NAME: &str = "WEBSITE_OWNER_NAME";
9const WEBSITE_SITE_NAME: &str = "WEBSITE_SITE_NAME";
10const WEBSITE_RESOURCE_GROUP: &str = "WEBSITE_RESOURCE_GROUP";
11const SITE_EXTENSION_VERSION: &str = "DD_AAS_DOTNET_EXTENSION_VERSION";
12const WEBSITE_OS: &str = "WEBSITE_OS";
13const INSTANCE_NAME: &str = "COMPUTERNAME";
14const INSTANCE_ID: &str = "WEBSITE_INSTANCE_ID";
15const SERVICE_CONTEXT: &str = "DD_AZURE_APP_SERVICES";
16const FUNCTIONS_WORKER_RUNTIME: &str = "FUNCTIONS_WORKER_RUNTIME";
17const FUNCTIONS_WORKER_RUNTIME_VERSION: &str = "FUNCTIONS_WORKER_RUNTIME_VERSION";
18const FUNCTIONS_EXTENSION_VERSION: &str = "FUNCTIONS_EXTENSION_VERSION";
19const DD_AZURE_RESOURCE_GROUP: &str = "DD_AZURE_RESOURCE_GROUP";
20const WEBSITE_SKU: &str = "WEBSITE_SKU";
21
22const UNKNOWN_VALUE: &str = "unknown";
23
24enum AzureContext {
25 AzureFunctions,
26 AzureAppService,
27}
28
29macro_rules! get_trimmed_env_var {
30 ($name:expr) => {
31 env::var($name)
32 .ok()
33 .map(|v| v.trim().to_string())
34 .filter(|s| !s.is_empty())
35 };
36}
37
38macro_rules! get_value_or_unknown {
39 ($name:expr) => {
40 $name.as_ref().map(|s| s.as_str()).unwrap_or(UNKNOWN_VALUE)
41 };
42}
43
44trait ToBoolean {
45 fn to_bool(&self) -> bool;
46}
47
48impl ToBoolean for String {
49 fn to_bool(&self) -> bool {
50 matches!(
51 self.to_lowercase().as_str(),
52 "true" | "t" | "y" | "1" | "yes"
53 )
54 }
55}
56
57pub trait QueryEnv {
58 fn get_var(&self, var: &str) -> Option<String>;
59}
60
61struct RealEnv;
62
63impl QueryEnv for RealEnv {
64 fn get_var(&self, var: &str) -> Option<String> {
65 get_trimmed_env_var!(var)
66 }
67}
68
69#[derive(Default)]
70pub struct AzureMetadata {
71 resource_id: Option<String>,
72 subscription_id: Option<String>,
73 site_name: Option<String>,
74 resource_group: Option<String>,
75 extension_version: Option<String>,
76 operating_system: String,
77 instance_name: Option<String>,
78 instance_id: Option<String>,
79 site_kind: String,
80 site_type: String,
81 runtime: Option<String>,
82 runtime_version: Option<String>,
83 function_runtime_version: Option<String>,
84}
85
86impl AzureMetadata {
87 fn get_azure_context<T: QueryEnv>(query: &T) -> AzureContext {
88 match (
89 query.get_var(FUNCTIONS_WORKER_RUNTIME),
90 query.get_var(FUNCTIONS_EXTENSION_VERSION),
91 ) {
92 (Some(_), Some(_)) => AzureContext::AzureFunctions,
93 (Some(_), None) => AzureContext::AzureFunctions,
94 (None, Some(_)) => AzureContext::AzureFunctions,
95 (None, None) => AzureContext::AzureAppService,
96 }
97 }
98
99 fn extract_subscription_id(s: Option<String>) -> Option<String> {
100 s?.split('+')
101 .next()
102 .filter(|s| !s.trim().is_empty())
103 .map(|v| v.to_string())
104 }
105
106 fn extract_resource_group(s: Option<String>) -> Option<String> {
107 #[allow(clippy::unwrap_used)]
108 let re: Regex = Regex::new(r".+\+(.+)-.+webspace(-Linux)?").unwrap();
109
110 s.as_ref().and_then(|text| {
111 re.captures(text)
112 .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string()))
113 })
114 }
115
116 fn build_resource_id(
121 subscription_id: Option<&String>,
122 site_name: Option<&String>,
123 resource_group: Option<&String>,
124 ) -> Option<String> {
125 match (subscription_id, site_name, resource_group) {
126 (Some(id_sub), Some(sitename), Some(res_grp)) => Some(
127 format!("/subscriptions/{id_sub}/resourcegroups/{res_grp}/providers/microsoft.web/sites/{sitename}")
128 .to_lowercase(),
129 ),
130 _ => None,
131 }
132 }
133
134 fn build_metadata<T: QueryEnv>(query: T) -> Option<Self> {
135 let subscription_id =
136 AzureMetadata::extract_subscription_id(query.get_var(WEBSITE_OWNER_NAME));
137 let site_name = query.get_var(WEBSITE_SITE_NAME);
138
139 let (site_kind, site_type) = match AzureMetadata::get_azure_context(&query) {
140 AzureContext::AzureFunctions => ("functionapp".to_owned(), "function".to_owned()),
141 _ => ("app".to_owned(), "app".to_owned()),
142 };
143
144 let resource_group = query
145 .get_var(DD_AZURE_RESOURCE_GROUP)
146 .or_else(|| query.get_var(WEBSITE_RESOURCE_GROUP))
147 .or_else(|| {
148 match query.get_var(WEBSITE_SKU).as_deref() {
150 Some("FlexConsumption") => None,
151 _ => AzureMetadata::extract_resource_group(query.get_var(WEBSITE_OWNER_NAME)),
156 }
157 });
158
159 let resource_id = AzureMetadata::build_resource_id(
160 subscription_id.as_ref(),
161 site_name.as_ref(),
162 resource_group.as_ref(),
163 );
164 let extension_version = query.get_var(SITE_EXTENSION_VERSION);
165 let operating_system = query
166 .get_var(WEBSITE_OS)
167 .unwrap_or(std::env::consts::OS.to_string());
168 let instance_name = query.get_var(INSTANCE_NAME);
169 let instance_id = query.get_var(INSTANCE_ID);
170
171 let runtime = query.get_var(FUNCTIONS_WORKER_RUNTIME);
172 let runtime_version = query.get_var(FUNCTIONS_WORKER_RUNTIME_VERSION);
173 let function_runtime_version = query.get_var(FUNCTIONS_EXTENSION_VERSION);
174
175 Some(AzureMetadata {
176 resource_id,
177 subscription_id,
178 site_name,
179 resource_group,
180 extension_version,
181 operating_system,
182 instance_name,
183 instance_id,
184 site_kind,
185 site_type,
186 runtime,
187 runtime_version,
188 function_runtime_version,
189 })
190 }
191
192 pub fn new<T: QueryEnv>(query: T) -> Option<Self> {
193 let is_relevant = query
194 .get_var(SERVICE_CONTEXT)
195 .map(|s| s.to_bool())
196 .unwrap_or(false);
197
198 if !is_relevant {
199 return None;
200 }
201
202 AzureMetadata::build_metadata(query)
203 }
204
205 pub fn new_function<T: QueryEnv>(query: T) -> Option<Self> {
206 match matches!(
207 AzureMetadata::get_azure_context(&query),
208 AzureContext::AzureFunctions
209 ) {
210 true => AzureMetadata::build_metadata(query),
211 false => None,
212 }
213 }
214
215 pub fn get_resource_id(&self) -> &str {
216 get_value_or_unknown!(self.resource_id)
217 }
218
219 pub fn get_subscription_id(&self) -> &str {
220 get_value_or_unknown!(self.subscription_id)
221 }
222
223 pub fn get_site_name(&self) -> &str {
224 get_value_or_unknown!(self.site_name)
225 }
226
227 pub fn get_resource_group(&self) -> &str {
228 get_value_or_unknown!(self.resource_group)
229 }
230
231 pub fn get_extension_version(&self) -> &str {
232 get_value_or_unknown!(self.extension_version)
233 }
234
235 pub fn get_operating_system(&self) -> &str {
236 self.operating_system.as_str()
237 }
238
239 pub fn get_instance_name(&self) -> &str {
240 get_value_or_unknown!(self.instance_name)
241 }
242
243 pub fn get_instance_id(&self) -> &str {
244 get_value_or_unknown!(self.instance_id)
245 }
246
247 pub fn get_site_type(&self) -> &str {
248 self.site_type.as_str()
249 }
250
251 pub fn get_site_kind(&self) -> &str {
252 self.site_kind.as_str()
253 }
254
255 pub fn get_runtime(&self) -> &str {
256 get_value_or_unknown!(self.runtime)
257 }
258
259 pub fn get_runtime_version(&self) -> &str {
260 get_value_or_unknown!(self.runtime_version)
261 }
262
263 pub fn get_function_runtime_version(&self) -> &str {
264 get_value_or_unknown!(self.function_runtime_version)
265 }
266}
267
268pub static AAS_METADATA: LazyLock<Option<AzureMetadata>> =
269 LazyLock::new(|| AzureMetadata::new(RealEnv {}));
270
271pub static AAS_METADATA_FUNCTION: LazyLock<Option<AzureMetadata>> =
272 LazyLock::new(|| AzureMetadata::new_function(RealEnv {}));
273
274#[cfg(test)]
275mod tests {
276
277 use indexmap::IndexMap;
278
279 use crate::azure_app_services::{QueryEnv, WEBSITE_OWNER_NAME};
280
281 use super::*;
282
283 struct MockEnv {
284 pub env_vars: IndexMap<String, String>,
285 }
286
287 impl MockEnv {
288 pub fn new(vars: &[(&str, &str)]) -> Self {
289 let mut env_vars: IndexMap<String, String> = IndexMap::new();
290 vars.iter().for_each(|(name, value)| {
291 env_vars.insert(name.to_string(), value.to_string());
292 });
293
294 MockEnv { env_vars }
295 }
296 }
297
298 impl QueryEnv for MockEnv {
299 fn get_var(&self, var: &str) -> Option<String> {
300 self.env_vars.get(var).cloned()
301 }
302 }
303
304 #[test]
305 fn test_metadata_is_not_relevant_by_default() {
306 let mocked_env = MockEnv::new(&[]);
307
308 let metadata = AzureMetadata::new(mocked_env);
309 assert!(metadata.is_none());
310 }
311
312 #[test]
313 fn test_metadata_is_relevant_first() {
314 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "true")]);
315
316 let metadata = AzureMetadata::new(mocked_env);
317 assert!(metadata.is_some());
318 }
319
320 #[test]
321 fn test_metadata_is_relevant_second() {
322 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "t")]);
323
324 let metadata = AzureMetadata::new(mocked_env);
325 assert!(metadata.is_some());
326 }
327
328 #[test]
329 fn test_metadata_is_relevant_third() {
330 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "TrUe")]);
331
332 let metadata = AzureMetadata::new(mocked_env);
333 assert!(metadata.is_some());
334 }
335
336 #[test]
337 fn test_metadata_is_relevant_fourth() {
338 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "1")]);
339
340 let metadata = AzureMetadata::new(mocked_env);
341 assert!(metadata.is_some());
342 }
343
344 #[test]
345 fn test_metadata_is_relevant_fifth() {
346 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "yEs")]);
347
348 let metadata = AzureMetadata::new(mocked_env);
349 assert!(metadata.is_some());
350 }
351
352 #[test]
353 fn test_metadata_is_relevant_sixth() {
354 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "Y")]);
355
356 let metadata = AzureMetadata::new(mocked_env);
357 assert!(metadata.is_some());
358 }
359
360 #[test]
361 fn test_metadata_is_not_relevant_if_explicit() {
362 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "0")]);
363
364 let metadata = AzureMetadata::new(mocked_env);
365 assert!(metadata.is_none());
366 }
367
368 #[test]
369 fn test_extract_subscription_without_plus_sign() {
370 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, "foo"), (SERVICE_CONTEXT, "1")]);
371
372 let metadata = AzureMetadata::new(mocked_env).unwrap();
373
374 let expected_id = "foo";
375
376 assert_eq!(metadata.get_subscription_id(), expected_id);
377 }
378
379 #[test]
380 fn test_extract_subscription_with_plus_sign() {
381 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, "foo+bar"), (SERVICE_CONTEXT, "1")]);
382
383 let metadata = AzureMetadata::new(mocked_env).unwrap();
384
385 let expected_id = "foo";
386 assert_eq!(metadata.get_subscription_id(), expected_id);
387 }
388
389 #[test]
390 fn test_extract_subscription_with_empty_string() {
391 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, ""), (SERVICE_CONTEXT, "1")]);
392
393 let metadata = AzureMetadata::new(mocked_env).unwrap();
394
395 assert_eq!(metadata.get_subscription_id(), UNKNOWN_VALUE);
396 }
397
398 #[test]
399 fn test_extract_subscription_with_only_whitespaces() {
400 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, " "), (SERVICE_CONTEXT, "1")]);
401
402 let metadata = AzureMetadata::new(mocked_env).unwrap();
403
404 assert_eq!(metadata.get_subscription_id(), UNKNOWN_VALUE);
405 }
406
407 #[test]
408 fn test_extract_subscription_with_only_plus_sign() {
409 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, "+"), (SERVICE_CONTEXT, "1")]);
410
411 let metadata = AzureMetadata::new(mocked_env).unwrap();
412
413 assert_eq!(metadata.get_subscription_id(), UNKNOWN_VALUE);
414 }
415
416 #[test]
417 fn test_extract_subscription_with_whitespaces_separated_by_plus() {
418 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, " + "), (SERVICE_CONTEXT, "1")]);
419
420 let metadata = AzureMetadata::new(mocked_env).unwrap();
421
422 assert_eq!(metadata.get_subscription_id(), UNKNOWN_VALUE);
423 }
424
425 #[test]
426 fn test_extract_subscription_plus_sign_and_other_string() {
427 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, "+other"), (SERVICE_CONTEXT, "1")]);
428
429 let metadata = AzureMetadata::new(mocked_env).unwrap();
430
431 assert_eq!(metadata.get_subscription_id(), UNKNOWN_VALUE);
432 }
433
434 #[test]
435 fn test_extract_resource_group_pattern_match_linux() {
436 let mocked_env = MockEnv::new(&[
437 (
438 WEBSITE_OWNER_NAME,
439 "00000000-0000-0000-0000-000000000000+test-rg-EastUSwebspace-Linux",
440 ),
441 ("FUNCTIONS_WORKER_RUNTIME", "node"),
442 ("FUNCTIONS_EXTENSION_VERSION", "~4"),
443 ]);
444
445 let metadata = AzureMetadata::new_function(mocked_env).unwrap();
446
447 let expected_resource_group = "test-rg";
448
449 assert_eq!(metadata.get_resource_group(), expected_resource_group);
450 }
451
452 #[test]
453 fn test_extract_resource_group_pattern_match_windows() {
454 let mocked_env = MockEnv::new(&[
455 (
456 WEBSITE_OWNER_NAME,
457 "00000000-0000-0000-0000-000000000000+test-rg-EastUSwebspace",
458 ),
459 ("FUNCTIONS_WORKER_RUNTIME", "node"),
460 ("FUNCTIONS_EXTENSION_VERSION", "~4"),
461 ]);
462
463 let metadata = AzureMetadata::new_function(mocked_env).unwrap();
464
465 let expected_resource_group = "test-rg";
466
467 assert_eq!(metadata.get_resource_group(), expected_resource_group);
468 }
469
470 #[test]
471 fn test_extract_resource_group_no_pattern_match() {
472 let mocked_env = MockEnv::new(&[
473 (WEBSITE_OWNER_NAME, "foo"),
474 (FUNCTIONS_WORKER_RUNTIME, "node"),
475 (FUNCTIONS_EXTENSION_VERSION, "~4"),
476 ]);
477
478 let metadata = AzureMetadata::new_function(mocked_env).unwrap();
479
480 assert_eq!(metadata.get_resource_group(), UNKNOWN_VALUE);
481 }
482
483 #[test]
484 fn test_use_resource_group_from_env_var_if_available() {
485 let mocked_env = MockEnv::new(&[
486 (WEBSITE_RESOURCE_GROUP, "test-rg-env-var"),
487 (
488 WEBSITE_OWNER_NAME,
489 "00000000-0000-0000-0000-000000000000+test-rg-EastUSwebspace-Linux",
490 ),
491 (SERVICE_CONTEXT, "1"),
492 (WEBSITE_SKU, "ElasticPremium"),
493 ]);
494
495 let metadata = AzureMetadata::new(mocked_env).unwrap();
496
497 let expected_resource_group = "test-rg-env-var";
498
499 assert_eq!(metadata.get_resource_group(), expected_resource_group);
500 }
501
502 #[test]
503 fn test_flex_consumption_resource_group_is_none_without_dd_azure_resource_group() {
504 let mocked_env = MockEnv::new(&[
505 (
506 WEBSITE_OWNER_NAME,
507 "00000000-0000-0000-0000-000000000000+flex-EastUSwebspace-Linux",
508 ),
509 (WEBSITE_SKU, "FlexConsumption"),
510 (SERVICE_CONTEXT, "1"),
511 ]);
512
513 let metadata = AzureMetadata::new(mocked_env).unwrap();
514
515 assert_eq!(metadata.get_resource_group(), UNKNOWN_VALUE);
516 }
517
518 #[test]
519 fn test_flex_consumption_uses_dd_azure_resource_group() {
520 let mocked_env = MockEnv::new(&[
521 (
522 WEBSITE_OWNER_NAME,
523 "00000000-0000-0000-0000-000000000000+flex-EastUSwebspace-Linux",
524 ),
525 (DD_AZURE_RESOURCE_GROUP, "test-flex-rg"),
526 (WEBSITE_SKU, "FlexConsumption"),
527 (SERVICE_CONTEXT, "1"),
528 ]);
529
530 let metadata = AzureMetadata::new(mocked_env).unwrap();
531
532 assert_eq!(metadata.get_resource_group(), "test-flex-rg");
535 }
536
537 #[test]
538 fn test_dd_azure_resource_group_has_highest_priority() {
539 let mocked_env = MockEnv::new(&[
540 (WEBSITE_RESOURCE_GROUP, "test-rg-env-var"),
541 (
542 WEBSITE_OWNER_NAME,
543 "00000000-0000-0000-0000-000000000000+test-rg-EastUSwebspace-Linux",
544 ),
545 (DD_AZURE_RESOURCE_GROUP, "dd-azure-rg-override"),
546 (SERVICE_CONTEXT, "1"),
547 ]);
548
549 let metadata = AzureMetadata::new(mocked_env).unwrap();
550
551 let expected_resource_group = "dd-azure-rg-override";
554
555 assert_eq!(metadata.get_resource_group(), expected_resource_group);
556 }
557
558 #[test]
559 fn test_build_resource_id() {
560 let mocked_env = MockEnv::new(&[
561 (WEBSITE_OWNER_NAME, "foo"),
562 (WEBSITE_SITE_NAME, "my_website"),
563 (WEBSITE_RESOURCE_GROUP, "resource_group"),
564 (SERVICE_CONTEXT, "1"),
565 ]);
566
567 let metadata = AzureMetadata::new(mocked_env).unwrap();
568
569 assert_eq!(
570 metadata.get_resource_id(),
571 "/subscriptions/foo/resourcegroups/resource_group/providers/microsoft.web/sites/my_website"
572 )
573 }
574
575 #[test]
576 fn test_build_resource_id_with_missing_subscription_id() {
577 let mocked_env = MockEnv::new(&[
578 (WEBSITE_SITE_NAME, "my_website"),
579 (WEBSITE_RESOURCE_GROUP, "resource_group"),
580 (SERVICE_CONTEXT, "1"),
581 ]);
582
583 let metadata = AzureMetadata::new(mocked_env).unwrap();
584
585 assert_eq!(metadata.get_resource_id(), UNKNOWN_VALUE)
586 }
587
588 #[test]
589 fn test_build_resource_id_with_missing_site_name() {
590 let mocked_env = MockEnv::new(&[
591 (WEBSITE_OWNER_NAME, "foo"),
592 (WEBSITE_RESOURCE_GROUP, "resource_group"),
593 (SERVICE_CONTEXT, "1"),
594 ]);
595
596 let metadata = AzureMetadata::new(mocked_env).unwrap();
597
598 assert_eq!(metadata.get_resource_id(), UNKNOWN_VALUE)
599 }
600
601 #[test]
602 fn test_build_resource_id_with_missing_resource_group() {
603 let mocked_env = MockEnv::new(&[
604 (WEBSITE_OWNER_NAME, "foo"),
605 (WEBSITE_SITE_NAME, "my_website"),
606 (SERVICE_CONTEXT, "1"),
607 ]);
608
609 let metadata = AzureMetadata::new(mocked_env).unwrap();
610
611 assert_eq!(metadata.get_resource_id(), UNKNOWN_VALUE)
612 }
613
614 #[test]
615 fn test_build_resource_id_with_missing_info() {
616 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "1")]);
617 let metadata = AzureMetadata::new(mocked_env).unwrap();
618
619 assert_eq!(metadata.get_resource_id(), UNKNOWN_VALUE)
620 }
621
622 #[test]
623 fn test_site_type_and_kind_default() {
624 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "1")]);
625 let metadata = AzureMetadata::new(mocked_env).unwrap();
626
627 assert_eq!(metadata.get_site_type(), "app");
628 assert_eq!(metadata.get_site_kind(), "app")
629 }
630
631 #[test]
632 fn test_site_type_and_kind_if_worker_runtime_not_specified() {
633 let mocked_env = MockEnv::new(&[
634 (FUNCTIONS_WORKER_RUNTIME, "my_runtime"),
635 (SERVICE_CONTEXT, "1"),
636 ]);
637 let metadata = AzureMetadata::new(mocked_env).unwrap();
638
639 assert_eq!(metadata.get_site_kind(), "functionapp");
640 assert_eq!(metadata.get_site_type(), "function")
641 }
642
643 #[test]
644 fn test_site_type_and_kind_if_extension_version_not_specified() {
645 let mocked_env = MockEnv::new(&[
646 (FUNCTIONS_EXTENSION_VERSION, "next_version"),
647 (SERVICE_CONTEXT, "1"),
648 ]);
649 let metadata = AzureMetadata::new(mocked_env).unwrap();
650
651 assert_eq!(metadata.get_site_kind(), "functionapp");
652 assert_eq!(metadata.get_site_type(), "function")
653 }
654
655 #[test]
656 fn test_site_type_and_kind_if_both_specified() {
657 let mocked_env = MockEnv::new(&[
658 (FUNCTIONS_WORKER_RUNTIME, "my_runtime"),
659 (FUNCTIONS_EXTENSION_VERSION, "next_version"),
660 (SERVICE_CONTEXT, "1"),
661 ]);
662 let metadata = AzureMetadata::new(mocked_env).unwrap();
663
664 assert_eq!(metadata.get_site_kind(), "functionapp");
665 assert_eq!(metadata.get_site_type(), "function")
666 }
667
668 #[test]
669 fn test_check_other_simple_env_retrieval() {
670 let expected_site_name = "my_site_name".to_owned();
671 let expected_resource_group = "my_resource_group".to_owned();
672 let expected_site_version = "v42".to_owned();
673 let expected_operating_system = "FreeBSD".to_owned();
674 let expected_instance_name = "my_instance_name".to_owned();
675 let expected_instance_id = "my_instance_id".to_owned();
676 let expected_function_extension_version = "~4".to_owned();
677 let expected_runtime = "node".to_owned();
678 let expected_runtime_version = "18".to_owned();
679
680 let mocked_env = MockEnv::new(&[
681 (WEBSITE_SITE_NAME, expected_site_name.as_str()),
682 (WEBSITE_RESOURCE_GROUP, expected_resource_group.as_str()),
683 (SITE_EXTENSION_VERSION, expected_site_version.as_str()),
684 (WEBSITE_OS, expected_operating_system.as_str()),
685 (INSTANCE_NAME, expected_instance_name.as_str()),
686 (INSTANCE_ID, expected_instance_id.as_str()),
687 (SERVICE_CONTEXT, "1"),
688 (
689 FUNCTIONS_EXTENSION_VERSION,
690 expected_function_extension_version.as_str(),
691 ),
692 (FUNCTIONS_WORKER_RUNTIME, expected_runtime.as_str()),
693 (
694 FUNCTIONS_WORKER_RUNTIME_VERSION,
695 expected_runtime_version.as_str(),
696 ),
697 ]);
698
699 let metadata = AzureMetadata::new(mocked_env).unwrap();
700
701 assert_eq!(expected_site_name, metadata.get_site_name());
702 assert_eq!(expected_resource_group, metadata.get_resource_group());
703 assert_eq!(expected_site_version, metadata.get_extension_version());
704 assert_eq!(expected_operating_system, metadata.get_operating_system());
705 assert_eq!(expected_instance_name, metadata.get_instance_name());
706 assert_eq!(expected_instance_id, metadata.get_instance_id());
707 assert_eq!(
708 expected_function_extension_version,
709 metadata.get_function_runtime_version()
710 );
711 assert_eq!(expected_runtime, metadata.get_runtime());
712 assert_eq!(expected_runtime_version, metadata.get_runtime_version());
713 }
714
715 #[test]
716 fn test_get_trimmed_env_var_empty_string() {
717 env::remove_var("TEST_VAR_NONE");
718 assert_eq!(get_trimmed_env_var!("TEST_VAR_NONE"), None);
719
720 env::set_var("TEST_VAR_EMPTY_STRING", "");
721 assert_eq!(get_trimmed_env_var!("TEST_VAR_EMPTY_STRING"), None);
722 }
723}