aws_config/imds/
region.rs
1use crate::imds::{self, Client};
12use crate::meta::region::{future, ProvideRegion};
13use crate::provider_config::ProviderConfig;
14use aws_smithy_types::error::display::DisplayErrorContext;
15use aws_types::os_shim_internal::Env;
16use aws_types::region::Region;
17use tracing::Instrument;
18
19#[derive(Debug)]
23pub struct ImdsRegionProvider {
24 client: Client,
25 env: Env,
26}
27
28const REGION_PATH: &str = "/latest/meta-data/placement/region";
29
30impl ImdsRegionProvider {
31 pub fn builder() -> Builder {
33 Builder::default()
34 }
35
36 fn imds_disabled(&self) -> bool {
37 match self.env.get(super::env::EC2_METADATA_DISABLED) {
38 Ok(value) => value.eq_ignore_ascii_case("true"),
39 _ => false,
40 }
41 }
42
43 pub async fn region(&self) -> Option<Region> {
47 if self.imds_disabled() {
48 tracing::debug!("not using IMDS to load region, IMDS is disabled");
49 return None;
50 }
51 match self.client.get(REGION_PATH).await {
52 Ok(region) => {
53 tracing::debug!(region = %region.as_ref(), "loaded region from IMDS");
54 Some(Region::new(String::from(region)))
55 }
56 Err(err) => {
57 tracing::warn!(err = %DisplayErrorContext(&err), "failed to load region from IMDS");
58 None
59 }
60 }
61 }
62}
63
64impl ProvideRegion for ImdsRegionProvider {
65 fn region(&self) -> future::ProvideRegion<'_> {
66 future::ProvideRegion::new(
67 self.region()
68 .instrument(tracing::debug_span!("imds_load_region")),
69 )
70 }
71}
72
73#[derive(Debug, Default)]
75pub struct Builder {
76 provider_config: Option<ProviderConfig>,
77 imds_client_override: Option<imds::Client>,
78}
79
80impl Builder {
81 pub fn configure(self, provider_config: &ProviderConfig) -> Self {
83 Self {
84 provider_config: Some(provider_config.clone()),
85 ..self
86 }
87 }
88
89 pub fn imds_client(mut self, imds_client: imds::Client) -> Self {
91 self.imds_client_override = Some(imds_client);
92 self
93 }
94
95 pub fn build(self) -> ImdsRegionProvider {
97 let provider_config = self.provider_config.unwrap_or_default();
98 let client = self
99 .imds_client_override
100 .unwrap_or_else(|| imds::Client::builder().configure(&provider_config).build());
101 ImdsRegionProvider {
102 client,
103 env: provider_config.env(),
104 }
105 }
106}
107
108#[cfg(test)]
109mod test {
110 use crate::imds::client::test::{imds_request, imds_response, token_request, token_response};
111 use crate::imds::region::ImdsRegionProvider;
112 use crate::provider_config::ProviderConfig;
113 use aws_smithy_async::rt::sleep::TokioSleep;
114 use aws_smithy_runtime::client::http::test_util::{ReplayEvent, StaticReplayClient};
115 use aws_smithy_types::body::SdkBody;
116 use aws_types::region::Region;
117 use tracing_test::traced_test;
118
119 #[tokio::test]
120 async fn load_region() {
121 let http_client = StaticReplayClient::new(vec![
122 ReplayEvent::new(
123 token_request("http://169.254.169.254", 21600),
124 token_response(21600, "token"),
125 ),
126 ReplayEvent::new(
127 imds_request(
128 "http://169.254.169.254/latest/meta-data/placement/region",
129 "token",
130 ),
131 imds_response("eu-west-1"),
132 ),
133 ]);
134 let provider = ImdsRegionProvider::builder()
135 .configure(
136 &ProviderConfig::no_configuration()
137 .with_http_client(http_client)
138 .with_sleep_impl(TokioSleep::new()),
139 )
140 .build();
141 assert_eq!(
142 provider.region().await.expect("returns region"),
143 Region::new("eu-west-1")
144 );
145 }
146
147 #[traced_test]
148 #[tokio::test]
149 async fn no_region_imds_disabled() {
150 let http_client = StaticReplayClient::new(vec![ReplayEvent::new(
151 token_request("http://169.254.169.254", 21600),
152 http::Response::builder()
153 .status(403)
154 .body(SdkBody::empty())
155 .unwrap(),
156 )]);
157 let provider = ImdsRegionProvider::builder()
158 .configure(
159 &ProviderConfig::no_configuration()
160 .with_http_client(http_client)
161 .with_sleep_impl(TokioSleep::new()),
162 )
163 .build();
164 assert_eq!(provider.region().await, None);
165 assert!(logs_contain("failed to load region from IMDS"));
166 assert!(logs_contain("IMDS is disabled"));
167 }
168}