mountpoint_s3_client/
imds_crt_client.rs1use futures::channel::oneshot;
4use mountpoint_s3_crt::auth::imds_client::{ImdsClient, ImdsClientConfig};
5use mountpoint_s3_crt::common::allocator::Allocator;
6use mountpoint_s3_crt::common::error;
7use mountpoint_s3_crt::io::channel_bootstrap::{ClientBootstrap, ClientBootstrapOptions};
8use mountpoint_s3_crt::io::event_loop::EventLoopGroup;
9use mountpoint_s3_crt::io::host_resolver::{HostResolver, HostResolverDefaultOptions};
10use serde_json::Value;
11use thiserror::Error;
12
13#[derive(Debug)]
14pub struct ImdsCrtClient {
16 imds_client: ImdsClient,
17 #[allow(unused)]
18 event_loop_group: EventLoopGroup,
19 #[allow(unused)]
20 allocator: Allocator,
21}
22
23impl ImdsCrtClient {
24 pub fn new() -> Result<Self, error::Error> {
25 let allocator = Allocator::default();
26
27 let mut event_loop_group = EventLoopGroup::new_default(&allocator, None, || {}).unwrap();
28
29 let resolver_options = HostResolverDefaultOptions {
30 max_entries: 8,
31 event_loop_group: &mut event_loop_group,
32 };
33
34 let mut host_resolver = HostResolver::new_default(&allocator, &resolver_options).unwrap();
35
36 let bootstrap_options = ClientBootstrapOptions {
37 event_loop_group: &mut event_loop_group,
38 host_resolver: &mut host_resolver,
39 };
40
41 let client_bootstrap = ClientBootstrap::new(&allocator, &bootstrap_options).unwrap();
42
43 let mut client_config = ImdsClientConfig::new();
44 client_config.client_bootstrap(client_bootstrap);
45
46 let imds_client = ImdsClient::new(&allocator, client_config)?;
47
48 Ok(Self {
49 imds_client,
50 event_loop_group,
51 allocator,
52 })
53 }
54
55 pub async fn get_identity_document(&self) -> Result<IdentityDocument, ImdsQueryRequestError> {
57 const IDENTITY_DOCUMENT_PATH: &str = "/latest/dynamic/instance-identity/document";
58
59 let (tx, rx) = oneshot::channel();
60 self.imds_client.get_resource(IDENTITY_DOCUMENT_PATH, move |result| {
61 let _ = tx.send(result);
62 })?;
63
64 let json = match rx.await {
65 Ok(Ok(json)) => json,
66 Ok(Err(err)) => return Err(ImdsQueryRequestError::CrtError(err)),
67 Err(err) => return Err(ImdsQueryRequestError::InternalError(Box::new(err))),
68 };
69
70 let json = &json;
71 let parsed: Value =
72 serde_json::from_str(json).map_err(|_| ImdsQueryRequestError::InvalidResponse(json.to_owned()))?;
73 let instance_type = parsed
74 .get("instanceType")
75 .and_then(Value::as_str)
76 .ok_or_else(|| ImdsQueryRequestError::InvalidResponse(json.to_owned()))?
77 .to_owned();
78 let region = parsed
79 .get("region")
80 .and_then(Value::as_str)
81 .ok_or_else(|| ImdsQueryRequestError::InvalidResponse(json.to_owned()))?
82 .to_owned();
83
84 Ok(IdentityDocument { instance_type, region })
85 }
86}
87
88#[derive(Debug)]
90pub struct IdentityDocument {
91 pub instance_type: String,
94 pub region: String,
96}
97
98#[derive(Error, Debug)]
100pub enum ImdsQueryRequestError {
101 #[error("Internal Imds client error")]
103 InternalError(#[source] Box<dyn std::error::Error + Send + Sync>),
104
105 #[error("Unknown CRT error")]
107 CrtError(#[from] error::Error),
108
109 #[error("Invalid response from Imds: {0}")]
111 InvalidResponse(String),
112}