diff --git a/sdk/aws-config/src/default_provider.rs b/sdk/aws-config/src/default_provider.rs index 5cbf1102ba1e..104b2b864b3c 100644 --- a/sdk/aws-config/src/default_provider.rs +++ b/sdk/aws-config/src/default_provider.rs @@ -51,3 +51,6 @@ pub mod use_dual_stack; /// Default access token provider chain #[cfg(feature = "sso")] pub mod token; + +/// Default endpoint-url provider chain +pub mod endpoint_url; diff --git a/sdk/aws-config/src/default_provider/endpoint_url.rs b/sdk/aws-config/src/default_provider/endpoint_url.rs new file mode 100644 index 000000000000..0f2db2ae5e32 --- /dev/null +++ b/sdk/aws-config/src/default_provider/endpoint_url.rs @@ -0,0 +1,125 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::environment::parse_bool; +use crate::provider_config::ProviderConfig; +use crate::standard_property::StandardProperty; +use aws_smithy_types::error::display::DisplayErrorContext; + +mod env { + pub(super) const ENDPOINT_URL: &str = "AWS_ENDPOINT_URL"; + pub(super) const IGNORE_CONFIGURED_ENDPOINT_URLS: &str = "AWS_IGNORE_CONFIGURED_ENDPOINT_URLS"; +} + +mod profile_key { + pub(super) const ENDPOINT_URL: &str = "endpoint_url"; + pub(super) const IGNORE_CONFIGURED_ENDPOINT_URLS: &str = "ignore_configured_endpoint_urls"; +} + +/// Load the value for "endpoint_url" +/// +/// This checks the following sources: +/// 1. The environment variable `AWS_ENDPOINT_URL=http://localhost:1234' +/// 2. The profile key `endpoint_url=http://localhost:1234` +pub async fn use_endpoint_url_provider(provider_config: &ProviderConfig) -> Option { + let ignore: bool = StandardProperty::new() + .env(env::IGNORE_CONFIGURED_ENDPOINT_URLS) + .profile(profile_key::IGNORE_CONFIGURED_ENDPOINT_URLS) + .validate(provider_config, parse_bool) + .await + .map_err( + |err| tracing::warn!(err = %DisplayErrorContext(&err), "invalid value for ignore_configured_endpoint_urls setting"), + ) + .unwrap_or(Some(false)) + .unwrap_or_default(); + if ignore { + return None; + } + StandardProperty::new() + .env(env::ENDPOINT_URL) + .profile(profile_key::ENDPOINT_URL) + .load(provider_config) + .await + .map(|(v, _ctx)| return v.as_ref().to_string()) +} + +#[cfg(test)] +mod test { + use crate::default_provider::endpoint_url::use_endpoint_url_provider; + use crate::profile::profile_file::{ProfileFileKind, ProfileFiles}; + use crate::provider_config::ProviderConfig; + use aws_types::os_shim_internal::{Env, Fs}; + use tracing_test::traced_test; + + #[tokio::test] + #[traced_test] + async fn defaults_to_none() { + let conf = ProviderConfig::empty(); + assert_eq!(use_endpoint_url_provider(&conf).await, None); + } + + #[tokio::test] + #[traced_test] + async fn environment_priority() { + let conf = ProviderConfig::empty() + .with_env(Env::from_slice(&[( + "AWS_ENDPOINT_URL", + "http://localhost:1", + )])) + .with_profile_config( + Some( + ProfileFiles::builder() + .with_file(ProfileFileKind::Config, "conf") + .build(), + ), + None, + ) + .with_fs(Fs::from_slice(&[( + "conf", + "[default]\nendpoint_url = http://localhost:2", + )])); + assert_eq!( + use_endpoint_url_provider(&conf).await.unwrap(), + "http://localhost:1".to_string() + ); + } + + #[tokio::test] + #[traced_test] + async fn log_error_on_invalid_ignore() { + let conf = ProviderConfig::empty().with_env(Env::from_slice(&[( + "AWS_IGNORE_CONFIGURED_ENDPOINT_URLS", + "not-a-boolean", + )])); + assert_eq!(use_endpoint_url_provider(&conf).await, None); + assert!(logs_contain( + "invalid value for ignore_configured_endpoint_urls setting" + )); + assert!(logs_contain("AWS_IGNORE_CONFIGURED_ENDPOINT_URLS")); + } + + #[tokio::test] + #[traced_test] + async fn ignore_if_specified_explicitly() { + let conf = ProviderConfig::empty() + .with_env(Env::from_slice(&[ + ("AWS_ENDPOINT_URL", "http://localhost:1"), + ("AWS_IGNORE_CONFIGURED_ENDPOINT_URLS", "true"), + ])) + .with_profile_config( + Some( + ProfileFiles::builder() + .with_file(ProfileFileKind::Config, "conf") + .build(), + ), + None, + ) + .with_fs(Fs::from_slice(&[( + "conf", + "[default]\nendpoint_url = http://localhost:2", + )])); + assert_eq!(use_endpoint_url_provider(&conf).await, None); + } +} diff --git a/sdk/aws-config/src/lib.rs b/sdk/aws-config/src/lib.rs index f6d374d04f36..ab2e112b3717 100644 --- a/sdk/aws-config/src/lib.rs +++ b/sdk/aws-config/src/lib.rs @@ -208,6 +208,7 @@ pub async fn load_defaults(version: BehaviorVersion) -> SdkConfig { } mod loader { + use crate::default_provider::endpoint_url::use_endpoint_url_provider; use crate::default_provider::use_dual_stack::use_dual_stack_provider; use crate::default_provider::use_fips::use_fips_provider; use crate::default_provider::{app_name, credentials, region, retry_config, timeout_config}; @@ -749,6 +750,12 @@ mod loader { .await }; + let endpoint_url = if let Some(endpoint_url) = self.endpoint_url { + Some(endpoint_url) + } else { + use_endpoint_url_provider(&conf).await + }; + let retry_config = if let Some(retry_config) = self.retry_config { retry_config } else { @@ -818,7 +825,7 @@ mod loader { builder.set_credentials_provider(credentials_provider); builder.set_token_provider(token_provider); builder.set_sleep_impl(sleep_impl); - builder.set_endpoint_url(self.endpoint_url); + builder.set_endpoint_url(endpoint_url); builder.set_use_fips(use_fips); builder.set_use_dual_stack(use_dual_stack); builder.set_stalled_stream_protection(self.stalled_stream_protection_config); @@ -929,6 +936,13 @@ mod loader { assert_eq!(None, conf.use_dual_stack()); } + #[tokio::test] + async fn endpoint_url() { + let endpoint_url = "http://localhost:1234"; + let conf = base_conf().endpoint_url(endpoint_url).load().await; + assert_eq!(Some(endpoint_url), conf.endpoint_url()); + } + #[tokio::test] async fn app_name() { let app_name = AppName::new("my-app-name").unwrap();