Skip to main content

mountpoint_s3_client/
instance_info.rs

1//! A simple interface to retrieve information about the EC2 instance this client is running on by
2//! querying the Instance Metadata Service (IMDS).
3
4use std::cell::LazyCell;
5use std::env;
6
7use thiserror::Error;
8
9use crate::imds_crt_client::{IdentityDocument, ImdsCrtClient, ImdsQueryRequestError};
10
11/// Information on the EC2 instance from the IMDS client. The client is queried lazily and only if
12/// the `AWS_EC2_METADATA_DISABLED` environment variable is not set.
13#[derive(Debug)]
14pub struct InstanceInfo {
15    document: LazyCell<Result<IdentityDocument, InstanceInfoError>>,
16}
17
18impl InstanceInfo {
19    /// Create a new instance. The IMDS client will only be queried when a method on the instance is
20    /// called, and only if the `AWS_EC2_METADATA_DISABLED` environment variable is not set.
21    pub fn new() -> Self {
22        Self {
23            document: LazyCell::new(|| {
24                if !imds_disabled() {
25                    match retrieve_instance_identity_document() {
26                        Ok(identity_document) => {
27                            tracing::debug!(?identity_document, "got instance info from IMDS");
28                            Ok(identity_document)
29                        }
30                        Err(err) => {
31                            tracing::debug!("EC2 instance info not retrieved: {err:?}");
32                            Err(err)
33                        }
34                    }
35                } else {
36                    tracing::debug!("EC2 instance info not retrieved: IMDS was disabled");
37                    Err(InstanceInfoError::ImdsDisabled)
38                }
39            }),
40        }
41    }
42
43    /// The region for the current instance, if it can be retrieved using the IMDS client.
44    pub fn region(&self) -> Result<&str, &InstanceInfoError> {
45        self.document.as_ref().map(|d| d.region.as_str())
46    }
47
48    /// The instance type for the current instance, if it can be retrieved using the IMDS client.
49    pub fn instance_type(&self) -> Result<&str, &InstanceInfoError> {
50        self.document.as_ref().map(|d| d.instance_type.as_str())
51    }
52}
53
54impl Default for InstanceInfo {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60fn retrieve_instance_identity_document() -> Result<IdentityDocument, InstanceInfoError> {
61    let imds_crt_client = ImdsCrtClient::new().map_err(InstanceInfoError::ImdsClientFailed)?;
62
63    let identity_document = futures::executor::block_on(imds_crt_client.get_identity_document())?;
64    Ok(identity_document)
65}
66
67fn imds_disabled() -> bool {
68    match env::var_os("AWS_EC2_METADATA_DISABLED") {
69        Some(val) => !val.eq_ignore_ascii_case("false"),
70        None => false,
71    }
72}
73
74/// Errors returned by instance info queries
75#[derive(Debug, Error)]
76pub enum InstanceInfoError {
77    /// IMDS is disabled
78    #[error("IMDS is disabled")]
79    ImdsDisabled,
80
81    /// A query to IMDS failed
82    #[error("IMDS query failed: {0}")]
83    ImdsQueryFailed(#[from] ImdsQueryRequestError),
84
85    /// The IMDS client couldn't be constructed
86    #[error("could not construct IMDS client: {0}")]
87    ImdsClientFailed(mountpoint_s3_crt::common::error::Error),
88}