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 pub fn get_app_service_tags(&self) -> impl ExactSizeIterator<Item = (&'static str, &str)> {
270 [
271 (
272 "aas.environment.extension_version",
273 self.get_extension_version(),
274 ),
275 ("aas.environment.instance_id", self.get_instance_id()),
276 ("aas.environment.instance_name", self.get_instance_name()),
277 ("aas.environment.os", self.get_operating_system()),
278 ("aas.resource.group", self.get_resource_group()),
279 ("aas.resource.id", self.get_resource_id()),
280 ("aas.site.kind", self.get_site_kind()),
281 ("aas.site.name", self.get_site_name()),
282 ("aas.site.type", self.get_site_type()),
283 ("aas.subscription.id", self.get_subscription_id()),
284 ]
285 .into_iter()
286 }
287
288 pub fn get_function_tags(&self) -> impl ExactSizeIterator<Item = (&'static str, &str)> {
291 [
292 ("aas.environment.instance_id", self.get_instance_id()),
293 ("aas.environment.instance_name", self.get_instance_name()),
294 ("aas.environment.os", self.get_operating_system()),
295 ("aas.environment.runtime", self.get_runtime()),
296 (
297 "aas.environment.runtime_version",
298 self.get_runtime_version(),
299 ),
300 (
301 "aas.environment.function_runtime",
302 self.get_function_runtime_version(),
303 ),
304 ("aas.resource.group", self.get_resource_group()),
305 ("aas.resource.id", self.get_resource_id()),
306 ("aas.site.kind", self.get_site_kind()),
307 ("aas.site.name", self.get_site_name()),
308 ("aas.site.type", self.get_site_type()),
309 ("aas.subscription.id", self.get_subscription_id()),
310 ]
311 .into_iter()
312 }
313}
314
315pub static AAS_METADATA: LazyLock<Option<AzureMetadata>> =
316 LazyLock::new(|| AzureMetadata::new(RealEnv {}));
317
318pub static AAS_METADATA_FUNCTION: LazyLock<Option<AzureMetadata>> =
319 LazyLock::new(|| AzureMetadata::new_function(RealEnv {}));
320
321#[cfg(test)]
322mod tests {
323
324 use indexmap::IndexMap;
325
326 use crate::azure_app_services::{QueryEnv, WEBSITE_OWNER_NAME};
327
328 use super::*;
329
330 struct MockEnv {
331 pub env_vars: IndexMap<String, String>,
332 }
333
334 impl MockEnv {
335 pub fn new(vars: &[(&str, &str)]) -> Self {
336 let mut env_vars: IndexMap<String, String> = IndexMap::new();
337 vars.iter().for_each(|(name, value)| {
338 env_vars.insert(name.to_string(), value.to_string());
339 });
340
341 MockEnv { env_vars }
342 }
343 }
344
345 impl QueryEnv for MockEnv {
346 fn get_var(&self, var: &str) -> Option<String> {
347 self.env_vars.get(var).cloned()
348 }
349 }
350
351 #[test]
352 fn test_metadata_is_not_relevant_by_default() {
353 let mocked_env = MockEnv::new(&[]);
354
355 let metadata = AzureMetadata::new(mocked_env);
356 assert!(metadata.is_none());
357 }
358
359 #[test]
360 fn test_metadata_is_relevant_first() {
361 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "true")]);
362
363 let metadata = AzureMetadata::new(mocked_env);
364 assert!(metadata.is_some());
365 }
366
367 #[test]
368 fn test_metadata_is_relevant_second() {
369 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "t")]);
370
371 let metadata = AzureMetadata::new(mocked_env);
372 assert!(metadata.is_some());
373 }
374
375 #[test]
376 fn test_metadata_is_relevant_third() {
377 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "TrUe")]);
378
379 let metadata = AzureMetadata::new(mocked_env);
380 assert!(metadata.is_some());
381 }
382
383 #[test]
384 fn test_metadata_is_relevant_fourth() {
385 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "1")]);
386
387 let metadata = AzureMetadata::new(mocked_env);
388 assert!(metadata.is_some());
389 }
390
391 #[test]
392 fn test_metadata_is_relevant_fifth() {
393 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "yEs")]);
394
395 let metadata = AzureMetadata::new(mocked_env);
396 assert!(metadata.is_some());
397 }
398
399 #[test]
400 fn test_metadata_is_relevant_sixth() {
401 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "Y")]);
402
403 let metadata = AzureMetadata::new(mocked_env);
404 assert!(metadata.is_some());
405 }
406
407 #[test]
408 fn test_metadata_is_not_relevant_if_explicit() {
409 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "0")]);
410
411 let metadata = AzureMetadata::new(mocked_env);
412 assert!(metadata.is_none());
413 }
414
415 #[test]
416 fn test_extract_subscription_without_plus_sign() {
417 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, "foo"), (SERVICE_CONTEXT, "1")]);
418
419 let metadata = AzureMetadata::new(mocked_env).unwrap();
420
421 let expected_id = "foo";
422
423 assert_eq!(metadata.get_subscription_id(), expected_id);
424 }
425
426 #[test]
427 fn test_extract_subscription_with_plus_sign() {
428 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, "foo+bar"), (SERVICE_CONTEXT, "1")]);
429
430 let metadata = AzureMetadata::new(mocked_env).unwrap();
431
432 let expected_id = "foo";
433 assert_eq!(metadata.get_subscription_id(), expected_id);
434 }
435
436 #[test]
437 fn test_extract_subscription_with_empty_string() {
438 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, ""), (SERVICE_CONTEXT, "1")]);
439
440 let metadata = AzureMetadata::new(mocked_env).unwrap();
441
442 assert_eq!(metadata.get_subscription_id(), UNKNOWN_VALUE);
443 }
444
445 #[test]
446 fn test_extract_subscription_with_only_whitespaces() {
447 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, " "), (SERVICE_CONTEXT, "1")]);
448
449 let metadata = AzureMetadata::new(mocked_env).unwrap();
450
451 assert_eq!(metadata.get_subscription_id(), UNKNOWN_VALUE);
452 }
453
454 #[test]
455 fn test_extract_subscription_with_only_plus_sign() {
456 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, "+"), (SERVICE_CONTEXT, "1")]);
457
458 let metadata = AzureMetadata::new(mocked_env).unwrap();
459
460 assert_eq!(metadata.get_subscription_id(), UNKNOWN_VALUE);
461 }
462
463 #[test]
464 fn test_extract_subscription_with_whitespaces_separated_by_plus() {
465 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, " + "), (SERVICE_CONTEXT, "1")]);
466
467 let metadata = AzureMetadata::new(mocked_env).unwrap();
468
469 assert_eq!(metadata.get_subscription_id(), UNKNOWN_VALUE);
470 }
471
472 #[test]
473 fn test_extract_subscription_plus_sign_and_other_string() {
474 let mocked_env = MockEnv::new(&[(WEBSITE_OWNER_NAME, "+other"), (SERVICE_CONTEXT, "1")]);
475
476 let metadata = AzureMetadata::new(mocked_env).unwrap();
477
478 assert_eq!(metadata.get_subscription_id(), UNKNOWN_VALUE);
479 }
480
481 #[test]
482 fn test_extract_resource_group_pattern_match_linux() {
483 let mocked_env = MockEnv::new(&[
484 (
485 WEBSITE_OWNER_NAME,
486 "00000000-0000-0000-0000-000000000000+test-rg-EastUSwebspace-Linux",
487 ),
488 ("FUNCTIONS_WORKER_RUNTIME", "node"),
489 ("FUNCTIONS_EXTENSION_VERSION", "~4"),
490 ]);
491
492 let metadata = AzureMetadata::new_function(mocked_env).unwrap();
493
494 let expected_resource_group = "test-rg";
495
496 assert_eq!(metadata.get_resource_group(), expected_resource_group);
497 }
498
499 #[test]
500 fn test_extract_resource_group_pattern_match_windows() {
501 let mocked_env = MockEnv::new(&[
502 (
503 WEBSITE_OWNER_NAME,
504 "00000000-0000-0000-0000-000000000000+test-rg-EastUSwebspace",
505 ),
506 ("FUNCTIONS_WORKER_RUNTIME", "node"),
507 ("FUNCTIONS_EXTENSION_VERSION", "~4"),
508 ]);
509
510 let metadata = AzureMetadata::new_function(mocked_env).unwrap();
511
512 let expected_resource_group = "test-rg";
513
514 assert_eq!(metadata.get_resource_group(), expected_resource_group);
515 }
516
517 #[test]
518 fn test_extract_resource_group_no_pattern_match() {
519 let mocked_env = MockEnv::new(&[
520 (WEBSITE_OWNER_NAME, "foo"),
521 (FUNCTIONS_WORKER_RUNTIME, "node"),
522 (FUNCTIONS_EXTENSION_VERSION, "~4"),
523 ]);
524
525 let metadata = AzureMetadata::new_function(mocked_env).unwrap();
526
527 assert_eq!(metadata.get_resource_group(), UNKNOWN_VALUE);
528 }
529
530 #[test]
531 fn test_use_resource_group_from_env_var_if_available() {
532 let mocked_env = MockEnv::new(&[
533 (WEBSITE_RESOURCE_GROUP, "test-rg-env-var"),
534 (
535 WEBSITE_OWNER_NAME,
536 "00000000-0000-0000-0000-000000000000+test-rg-EastUSwebspace-Linux",
537 ),
538 (SERVICE_CONTEXT, "1"),
539 (WEBSITE_SKU, "ElasticPremium"),
540 ]);
541
542 let metadata = AzureMetadata::new(mocked_env).unwrap();
543
544 let expected_resource_group = "test-rg-env-var";
545
546 assert_eq!(metadata.get_resource_group(), expected_resource_group);
547 }
548
549 #[test]
550 fn test_flex_consumption_resource_group_is_none_without_dd_azure_resource_group() {
551 let mocked_env = MockEnv::new(&[
552 (
553 WEBSITE_OWNER_NAME,
554 "00000000-0000-0000-0000-000000000000+flex-EastUSwebspace-Linux",
555 ),
556 (WEBSITE_SKU, "FlexConsumption"),
557 (SERVICE_CONTEXT, "1"),
558 ]);
559
560 let metadata = AzureMetadata::new(mocked_env).unwrap();
561
562 assert_eq!(metadata.get_resource_group(), UNKNOWN_VALUE);
563 }
564
565 #[test]
566 fn test_flex_consumption_uses_dd_azure_resource_group() {
567 let mocked_env = MockEnv::new(&[
568 (
569 WEBSITE_OWNER_NAME,
570 "00000000-0000-0000-0000-000000000000+flex-EastUSwebspace-Linux",
571 ),
572 (DD_AZURE_RESOURCE_GROUP, "test-flex-rg"),
573 (WEBSITE_SKU, "FlexConsumption"),
574 (SERVICE_CONTEXT, "1"),
575 ]);
576
577 let metadata = AzureMetadata::new(mocked_env).unwrap();
578
579 assert_eq!(metadata.get_resource_group(), "test-flex-rg");
582 }
583
584 #[test]
585 fn test_dd_azure_resource_group_has_highest_priority() {
586 let mocked_env = MockEnv::new(&[
587 (WEBSITE_RESOURCE_GROUP, "test-rg-env-var"),
588 (
589 WEBSITE_OWNER_NAME,
590 "00000000-0000-0000-0000-000000000000+test-rg-EastUSwebspace-Linux",
591 ),
592 (DD_AZURE_RESOURCE_GROUP, "dd-azure-rg-override"),
593 (SERVICE_CONTEXT, "1"),
594 ]);
595
596 let metadata = AzureMetadata::new(mocked_env).unwrap();
597
598 let expected_resource_group = "dd-azure-rg-override";
601
602 assert_eq!(metadata.get_resource_group(), expected_resource_group);
603 }
604
605 #[test]
606 fn test_build_resource_id() {
607 let mocked_env = MockEnv::new(&[
608 (WEBSITE_OWNER_NAME, "foo"),
609 (WEBSITE_SITE_NAME, "my_website"),
610 (WEBSITE_RESOURCE_GROUP, "resource_group"),
611 (SERVICE_CONTEXT, "1"),
612 ]);
613
614 let metadata = AzureMetadata::new(mocked_env).unwrap();
615
616 assert_eq!(
617 metadata.get_resource_id(),
618 "/subscriptions/foo/resourcegroups/resource_group/providers/microsoft.web/sites/my_website"
619 )
620 }
621
622 #[test]
623 fn test_build_resource_id_with_missing_subscription_id() {
624 let mocked_env = MockEnv::new(&[
625 (WEBSITE_SITE_NAME, "my_website"),
626 (WEBSITE_RESOURCE_GROUP, "resource_group"),
627 (SERVICE_CONTEXT, "1"),
628 ]);
629
630 let metadata = AzureMetadata::new(mocked_env).unwrap();
631
632 assert_eq!(metadata.get_resource_id(), UNKNOWN_VALUE)
633 }
634
635 #[test]
636 fn test_build_resource_id_with_missing_site_name() {
637 let mocked_env = MockEnv::new(&[
638 (WEBSITE_OWNER_NAME, "foo"),
639 (WEBSITE_RESOURCE_GROUP, "resource_group"),
640 (SERVICE_CONTEXT, "1"),
641 ]);
642
643 let metadata = AzureMetadata::new(mocked_env).unwrap();
644
645 assert_eq!(metadata.get_resource_id(), UNKNOWN_VALUE)
646 }
647
648 #[test]
649 fn test_build_resource_id_with_missing_resource_group() {
650 let mocked_env = MockEnv::new(&[
651 (WEBSITE_OWNER_NAME, "foo"),
652 (WEBSITE_SITE_NAME, "my_website"),
653 (SERVICE_CONTEXT, "1"),
654 ]);
655
656 let metadata = AzureMetadata::new(mocked_env).unwrap();
657
658 assert_eq!(metadata.get_resource_id(), UNKNOWN_VALUE)
659 }
660
661 #[test]
662 fn test_build_resource_id_with_missing_info() {
663 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "1")]);
664 let metadata = AzureMetadata::new(mocked_env).unwrap();
665
666 assert_eq!(metadata.get_resource_id(), UNKNOWN_VALUE)
667 }
668
669 #[test]
670 fn test_site_type_and_kind_default() {
671 let mocked_env = MockEnv::new(&[(SERVICE_CONTEXT, "1")]);
672 let metadata = AzureMetadata::new(mocked_env).unwrap();
673
674 assert_eq!(metadata.get_site_type(), "app");
675 assert_eq!(metadata.get_site_kind(), "app")
676 }
677
678 #[test]
679 fn test_site_type_and_kind_if_worker_runtime_not_specified() {
680 let mocked_env = MockEnv::new(&[
681 (FUNCTIONS_WORKER_RUNTIME, "my_runtime"),
682 (SERVICE_CONTEXT, "1"),
683 ]);
684 let metadata = AzureMetadata::new(mocked_env).unwrap();
685
686 assert_eq!(metadata.get_site_kind(), "functionapp");
687 assert_eq!(metadata.get_site_type(), "function")
688 }
689
690 #[test]
691 fn test_site_type_and_kind_if_extension_version_not_specified() {
692 let mocked_env = MockEnv::new(&[
693 (FUNCTIONS_EXTENSION_VERSION, "next_version"),
694 (SERVICE_CONTEXT, "1"),
695 ]);
696 let metadata = AzureMetadata::new(mocked_env).unwrap();
697
698 assert_eq!(metadata.get_site_kind(), "functionapp");
699 assert_eq!(metadata.get_site_type(), "function")
700 }
701
702 #[test]
703 fn test_site_type_and_kind_if_both_specified() {
704 let mocked_env = MockEnv::new(&[
705 (FUNCTIONS_WORKER_RUNTIME, "my_runtime"),
706 (FUNCTIONS_EXTENSION_VERSION, "next_version"),
707 (SERVICE_CONTEXT, "1"),
708 ]);
709 let metadata = AzureMetadata::new(mocked_env).unwrap();
710
711 assert_eq!(metadata.get_site_kind(), "functionapp");
712 assert_eq!(metadata.get_site_type(), "function")
713 }
714
715 #[test]
716 fn test_check_other_simple_env_retrieval() {
717 let expected_site_name = "my_site_name".to_owned();
718 let expected_resource_group = "my_resource_group".to_owned();
719 let expected_site_version = "v42".to_owned();
720 let expected_operating_system = "FreeBSD".to_owned();
721 let expected_instance_name = "my_instance_name".to_owned();
722 let expected_instance_id = "my_instance_id".to_owned();
723 let expected_function_extension_version = "~4".to_owned();
724 let expected_runtime = "node".to_owned();
725 let expected_runtime_version = "18".to_owned();
726
727 let mocked_env = MockEnv::new(&[
728 (WEBSITE_SITE_NAME, expected_site_name.as_str()),
729 (WEBSITE_RESOURCE_GROUP, expected_resource_group.as_str()),
730 (SITE_EXTENSION_VERSION, expected_site_version.as_str()),
731 (WEBSITE_OS, expected_operating_system.as_str()),
732 (INSTANCE_NAME, expected_instance_name.as_str()),
733 (INSTANCE_ID, expected_instance_id.as_str()),
734 (SERVICE_CONTEXT, "1"),
735 (
736 FUNCTIONS_EXTENSION_VERSION,
737 expected_function_extension_version.as_str(),
738 ),
739 (FUNCTIONS_WORKER_RUNTIME, expected_runtime.as_str()),
740 (
741 FUNCTIONS_WORKER_RUNTIME_VERSION,
742 expected_runtime_version.as_str(),
743 ),
744 ]);
745
746 let metadata = AzureMetadata::new(mocked_env).unwrap();
747
748 assert_eq!(expected_site_name, metadata.get_site_name());
749 assert_eq!(expected_resource_group, metadata.get_resource_group());
750 assert_eq!(expected_site_version, metadata.get_extension_version());
751 assert_eq!(expected_operating_system, metadata.get_operating_system());
752 assert_eq!(expected_instance_name, metadata.get_instance_name());
753 assert_eq!(expected_instance_id, metadata.get_instance_id());
754 assert_eq!(
755 expected_function_extension_version,
756 metadata.get_function_runtime_version()
757 );
758 assert_eq!(expected_runtime, metadata.get_runtime());
759 assert_eq!(expected_runtime_version, metadata.get_runtime_version());
760 }
761
762 #[test]
763 fn test_get_app_service_tags() {
764 let expected_site_name = "my_site_name";
765 let expected_resource_group = "my_resource_group";
766 let expected_site_version = "v42";
767 let expected_operating_system = "FreeBSD";
768 let expected_instance_name = "my_instance_name";
769 let expected_instance_id = "my_instance_id";
770 let expected_subscription_id = "sub-123";
771 let expected_resource_id = "/subscriptions/sub-123/resourcegroups/my_resource_group/providers/microsoft.web/sites/my_site_name";
772
773 let mocked_env = MockEnv::new(&[
774 (WEBSITE_SITE_NAME, expected_site_name),
775 (WEBSITE_RESOURCE_GROUP, expected_resource_group),
776 (SITE_EXTENSION_VERSION, expected_site_version),
777 (WEBSITE_OS, expected_operating_system),
778 (INSTANCE_NAME, expected_instance_name),
779 (INSTANCE_ID, expected_instance_id),
780 (SERVICE_CONTEXT, "1"),
781 (
782 WEBSITE_OWNER_NAME,
783 &format!("{}+rg-webspace", expected_subscription_id),
784 ),
785 ]);
786
787 let metadata = AzureMetadata::new(mocked_env).unwrap();
788
789 let tags: std::collections::HashMap<&str, &str> = metadata.get_app_service_tags().collect();
791
792 assert_eq!(tags.len(), 10);
794 assert_eq!(tags.get("aas.resource.id"), Some(&expected_resource_id));
795 assert_eq!(
796 tags.get("aas.environment.extension_version"),
797 Some(&expected_site_version)
798 );
799 assert_eq!(
800 tags.get("aas.environment.instance_id"),
801 Some(&expected_instance_id)
802 );
803 assert_eq!(
804 tags.get("aas.environment.instance_name"),
805 Some(&expected_instance_name)
806 );
807 assert_eq!(
808 tags.get("aas.environment.os"),
809 Some(&expected_operating_system)
810 );
811 assert_eq!(
812 tags.get("aas.resource.group"),
813 Some(&expected_resource_group)
814 );
815 assert_eq!(tags.get("aas.site.name"), Some(&expected_site_name));
816 assert_eq!(tags.get("aas.site.kind"), Some(&"app"));
817 assert_eq!(tags.get("aas.site.type"), Some(&"app"));
818 assert_eq!(
819 tags.get("aas.subscription.id"),
820 Some(&expected_subscription_id)
821 );
822
823 assert_eq!(tags.get("aas.environment.runtime"), None);
825 assert_eq!(tags.get("aas.environment.runtime_version"), None);
826 assert_eq!(tags.get("aas.environment.function_runtime"), None);
827
828 let iter = metadata.get_app_service_tags();
830 assert_eq!(iter.len(), 10);
831 }
832
833 #[test]
834 fn test_get_function_tags() {
835 let expected_site_name = "my_site_name";
836 let expected_resource_group = "my_resource_group";
837 let expected_operating_system = "FreeBSD";
838 let expected_instance_name = "my_instance_name";
839 let expected_instance_id = "my_instance_id";
840 let expected_function_extension_version = "~4";
841 let expected_runtime = "node";
842 let expected_runtime_version = "18";
843 let expected_subscription_id = "sub-123";
844 let expected_resource_id = "/subscriptions/sub-123/resourcegroups/my_resource_group/providers/microsoft.web/sites/my_site_name";
845
846 let mocked_env = MockEnv::new(&[
847 (WEBSITE_SITE_NAME, expected_site_name),
848 (WEBSITE_RESOURCE_GROUP, expected_resource_group),
849 (WEBSITE_OS, expected_operating_system),
850 (INSTANCE_NAME, expected_instance_name),
851 (INSTANCE_ID, expected_instance_id),
852 (SERVICE_CONTEXT, "1"),
853 (
854 FUNCTIONS_EXTENSION_VERSION,
855 expected_function_extension_version,
856 ),
857 (FUNCTIONS_WORKER_RUNTIME, expected_runtime),
858 (FUNCTIONS_WORKER_RUNTIME_VERSION, expected_runtime_version),
859 (
860 WEBSITE_OWNER_NAME,
861 &format!("{}+rg-webspace", expected_subscription_id),
862 ),
863 ]);
864
865 let metadata = AzureMetadata::new(mocked_env).unwrap();
866
867 let tags: std::collections::HashMap<&str, &str> = metadata.get_function_tags().collect();
869
870 assert_eq!(tags.len(), 12);
872 assert_eq!(tags.get("aas.resource.id"), Some(&expected_resource_id));
873 assert_eq!(
874 tags.get("aas.environment.instance_id"),
875 Some(&expected_instance_id)
876 );
877 assert_eq!(
878 tags.get("aas.environment.instance_name"),
879 Some(&expected_instance_name)
880 );
881 assert_eq!(
882 tags.get("aas.environment.os"),
883 Some(&expected_operating_system)
884 );
885 assert_eq!(tags.get("aas.environment.runtime"), Some(&expected_runtime));
886 assert_eq!(
887 tags.get("aas.environment.runtime_version"),
888 Some(&expected_runtime_version)
889 );
890 assert_eq!(
891 tags.get("aas.environment.function_runtime"),
892 Some(&expected_function_extension_version)
893 );
894 assert_eq!(
895 tags.get("aas.resource.group"),
896 Some(&expected_resource_group)
897 );
898 assert_eq!(tags.get("aas.site.name"), Some(&expected_site_name));
899 assert_eq!(tags.get("aas.site.kind"), Some(&"functionapp"));
900 assert_eq!(tags.get("aas.site.type"), Some(&"function"));
901 assert_eq!(
902 tags.get("aas.subscription.id"),
903 Some(&expected_subscription_id)
904 );
905
906 assert_eq!(tags.get("aas.environment.extension_version"), None);
908
909 let iter = metadata.get_function_tags();
911 assert_eq!(iter.len(), 12);
912 }
913
914 #[test]
915 fn test_get_trimmed_env_var_empty_string() {
916 env::remove_var("TEST_VAR_NONE");
917 assert_eq!(get_trimmed_env_var!("TEST_VAR_NONE"), None);
918
919 env::set_var("TEST_VAR_EMPTY_STRING", "");
920 assert_eq!(get_trimmed_env_var!("TEST_VAR_EMPTY_STRING"), None);
921 }
922}