Skip to content

Commit 0b35a09

Browse files
Zelda Hesslerjdisanti
Zelda Hessler
andauthored
Add support for env-defined endpoint URLs (#3488)
## Motivation and Context <!--- Why is this change required? What problem does it solve? --> <!--- If it fixes an open issue, please link to the issue here --> Part of the endpoint config work I'm doing ## Description <!--- Describe your changes in detail --> This change does two things: - add support for setting an endpoint URL from the env or profile file - add support for ignoring endpoint URLs sourced from the env and profile file ## Testing <!--- Please describe in detail how you tested your changes --> <!--- Include details of your testing environment, and the tests you ran to --> <!--- see how your change affects other areas of the code, etc. --> I wrote many unit tests. ## Checklist <!--- If a checkbox below is not applicable, then please DELETE it rather than leaving it unchecked --> - [x] I have updated `CHANGELOG.next.toml` if I made changes to the AWS SDK, generated SDK code, or SDK runtime crates ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti <[email protected]>
1 parent 4b9c9b7 commit 0b35a09

File tree

9 files changed

+354
-17
lines changed

9 files changed

+354
-17
lines changed

CHANGELOG.next.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,19 @@ message = "`DefaultS3ExpressIdentityProvider` now uses `BehaviorVersion` threade
2222
references = ["smithy-rs#3478"]
2323
meta = { "breaking" = false, "bug" = true, "tada" = false }
2424
author = "ysaito1001"
25+
26+
[[aws-sdk-rust]]
27+
message = """
28+
Users may now set an endpoint URL from the env or profile file:
29+
30+
- env: `AWS_ENDPOINT_URL="http://localhost"`
31+
- profile: `endpoint_url = http://localhost`
32+
33+
Users may also ignore endpoint URLs sourced from the env and profile files:
34+
35+
- env: `AWS_IGNORE_CONFIGURED_ENDPOINT_URLS="true"`
36+
- profile: `ignore_configured_endpoint_urls = true`
37+
"""
38+
references = ["smithy-rs#3488"]
39+
meta = { "breaking" = false, "tada" = true, "bug" = false }
40+
authors = ["Velfi"]

aws/rust-runtime/aws-config/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "aws-config"
3-
version = "1.1.8"
3+
version = "1.1.9"
44
authors = [
55
"AWS Rust SDK Team <[email protected]>",
66
"Russell Cohen <[email protected]>",
@@ -26,19 +26,20 @@ allow-compilation = []
2626

2727
[dependencies]
2828
aws-credential-types = { path = "../../sdk/build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] }
29+
aws-runtime = { path = "../../sdk/build/aws-sdk/sdk/aws-runtime" }
2930
aws-sdk-sts = { path = "../../sdk/build/aws-sdk/sdk/sts", default-features = false }
3031
aws-smithy-async = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-async" }
3132
aws-smithy-http = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-http" }
3233
aws-smithy-json = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-json" }
3334
aws-smithy-runtime = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-runtime", features = ["client"] }
3435
aws-smithy-runtime-api = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-runtime-api", features = ["client"] }
3536
aws-smithy-types = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-types" }
36-
aws-runtime = { path = "../../sdk/build/aws-sdk/sdk/aws-runtime" }
3737
aws-types = { path = "../../sdk/build/aws-sdk/sdk/aws-types" }
3838
hyper = { version = "0.14.26", default-features = false }
3939
time = { version = "0.3.4", features = ["parsing"] }
4040
tokio = { version = "1.13.1", features = ["sync"] }
4141
tracing = { version = "0.1" }
42+
url = "2.3.1"
4243

4344
# implementation detail of IMDS credentials provider
4445
fastrand = "2.0.0"
@@ -59,7 +60,7 @@ aws-sdk-ssooidc = { path = "../../sdk/build/aws-sdk/sdk/ssooidc", default-featur
5960
aws-smithy-runtime = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-runtime", features = ["client", "connector-hyper-0-14-x", "test-util"] }
6061
aws-smithy-runtime-api = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-runtime-api", features = ["test-util"] }
6162
futures-util = { version = "0.3.29", default-features = false }
62-
tracing-test = "0.2.1"
63+
tracing-test = "0.2.4"
6364
tracing-subscriber = { version = "0.3.16", features = ["fmt", "json"] }
6465

6566
tokio = { version = "1.23.1", features = ["full", "test-util"] }

aws/rust-runtime/aws-config/src/default_provider.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,9 @@ pub mod use_dual_stack;
5151
/// Default access token provider chain
5252
#[cfg(feature = "sso")]
5353
pub mod token;
54+
55+
/// Default "ignore configured endpoint URLs" provider chain
56+
pub mod ignore_configured_endpoint_urls;
57+
58+
/// Default endpoint URL provider chain
59+
pub mod endpoint_url;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
use crate::environment::parse_url;
7+
use crate::provider_config::ProviderConfig;
8+
use crate::standard_property::StandardProperty;
9+
use aws_smithy_types::error::display::DisplayErrorContext;
10+
11+
mod env {
12+
pub(super) const ENDPOINT_URL: &str = "AWS_ENDPOINT_URL";
13+
}
14+
15+
mod profile_key {
16+
pub(super) const ENDPOINT_URL: &str = "endpoint_url";
17+
}
18+
19+
/// Load the value for an endpoint URL
20+
///
21+
/// This checks the following sources:
22+
/// 1. The environment variable `AWS_ENDPOINT_URL=http://localhost`
23+
/// 2. The profile key `endpoint_url=http://localhost`
24+
///
25+
/// If invalid values are found, the provider will return None and an error will be logged.
26+
pub async fn endpoint_url_provider(provider_config: &ProviderConfig) -> Option<String> {
27+
StandardProperty::new()
28+
.env(env::ENDPOINT_URL)
29+
.profile(profile_key::ENDPOINT_URL)
30+
.validate(provider_config, parse_url)
31+
.await
32+
.map_err(
33+
|err| tracing::warn!(err = %DisplayErrorContext(&err), "invalid value for endpoint URL setting"),
34+
)
35+
.unwrap_or(None)
36+
}
37+
38+
#[cfg(test)]
39+
mod test {
40+
use super::endpoint_url_provider;
41+
use super::env;
42+
use crate::profile::profile_file::{ProfileFileKind, ProfileFiles};
43+
use crate::provider_config::ProviderConfig;
44+
use aws_types::os_shim_internal::{Env, Fs};
45+
use tracing_test::traced_test;
46+
47+
#[tokio::test]
48+
#[traced_test]
49+
async fn log_error_on_invalid_value() {
50+
let conf =
51+
ProviderConfig::empty().with_env(Env::from_slice(&[(env::ENDPOINT_URL, "not-a-url")]));
52+
assert_eq!(None, endpoint_url_provider(&conf).await);
53+
assert!(logs_contain("invalid value for endpoint URL setting"));
54+
assert!(logs_contain(env::ENDPOINT_URL));
55+
}
56+
57+
#[tokio::test]
58+
#[traced_test]
59+
async fn environment_priority() {
60+
let conf = ProviderConfig::empty()
61+
.with_env(Env::from_slice(&[(env::ENDPOINT_URL, "http://localhost")]))
62+
.with_profile_config(
63+
Some(
64+
ProfileFiles::builder()
65+
.with_file(ProfileFileKind::Config, "conf")
66+
.build(),
67+
),
68+
None,
69+
)
70+
.with_fs(Fs::from_slice(&[(
71+
"conf",
72+
"[default]\nendpoint_url = http://production",
73+
)]));
74+
assert_eq!(
75+
Some("http://localhost".to_owned()),
76+
endpoint_url_provider(&conf).await,
77+
);
78+
}
79+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
use crate::environment::parse_bool;
7+
use crate::provider_config::ProviderConfig;
8+
use crate::standard_property::StandardProperty;
9+
use aws_smithy_types::error::display::DisplayErrorContext;
10+
11+
mod env {
12+
pub(super) const IGNORE_CONFIGURED_ENDPOINT_URLS: &str = "AWS_IGNORE_CONFIGURED_ENDPOINT_URLS";
13+
}
14+
15+
mod profile_key {
16+
pub(super) const IGNORE_CONFIGURED_ENDPOINT_URLS: &str = "ignore_configured_endpoint_urls";
17+
}
18+
19+
/// Load the value for "ignore configured endpoint URLs"
20+
///
21+
/// This checks the following sources:
22+
/// 1. The environment variable `AWS_IGNORE_CONFIGURED_ENDPOINT_URLS_ENDPOINT=true/false`
23+
/// 2. The profile key `ignore_configured_endpoint_urls=true/false`
24+
///
25+
/// If invalid values are found, the provider will return None and an error will be logged.
26+
pub async fn ignore_configured_endpoint_urls_provider(
27+
provider_config: &ProviderConfig,
28+
) -> Option<bool> {
29+
StandardProperty::new()
30+
.env(env::IGNORE_CONFIGURED_ENDPOINT_URLS)
31+
.profile(profile_key::IGNORE_CONFIGURED_ENDPOINT_URLS)
32+
.validate(provider_config, parse_bool)
33+
.await
34+
.map_err(
35+
|err| tracing::warn!(err = %DisplayErrorContext(&err), "invalid value for 'ignore configured endpoint URLs' setting"),
36+
)
37+
.unwrap_or(None)
38+
}
39+
40+
#[cfg(test)]
41+
mod test {
42+
use super::env;
43+
use super::ignore_configured_endpoint_urls_provider;
44+
use crate::profile::profile_file::{ProfileFileKind, ProfileFiles};
45+
use crate::provider_config::ProviderConfig;
46+
use aws_types::os_shim_internal::{Env, Fs};
47+
use tracing_test::traced_test;
48+
49+
#[tokio::test]
50+
#[traced_test]
51+
async fn log_error_on_invalid_value() {
52+
let conf = ProviderConfig::empty().with_env(Env::from_slice(&[(
53+
env::IGNORE_CONFIGURED_ENDPOINT_URLS,
54+
"not-a-boolean",
55+
)]));
56+
assert_eq!(None, ignore_configured_endpoint_urls_provider(&conf).await,);
57+
assert!(logs_contain(
58+
"invalid value for 'ignore configured endpoint URLs' setting"
59+
));
60+
assert!(logs_contain(env::IGNORE_CONFIGURED_ENDPOINT_URLS));
61+
}
62+
63+
#[tokio::test]
64+
#[traced_test]
65+
async fn environment_priority() {
66+
let conf = ProviderConfig::empty()
67+
.with_env(Env::from_slice(&[(
68+
env::IGNORE_CONFIGURED_ENDPOINT_URLS,
69+
"TRUE",
70+
)]))
71+
.with_profile_config(
72+
Some(
73+
ProfileFiles::builder()
74+
.with_file(ProfileFileKind::Config, "conf")
75+
.build(),
76+
),
77+
None,
78+
)
79+
.with_fs(Fs::from_slice(&[(
80+
"conf",
81+
"[default]\nignore_configured_endpoint_urls = false",
82+
)]));
83+
assert_eq!(
84+
Some(true),
85+
ignore_configured_endpoint_urls_provider(&conf).await,
86+
);
87+
}
88+
}

aws/rust-runtime/aws-config/src/environment/mod.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//! Providers that load configuration from environment variables
77
88
use std::error::Error;
9-
use std::fmt::{Display, Formatter};
9+
use std::fmt;
1010

1111
/// Load credentials from the environment
1212
pub mod credentials;
@@ -21,9 +21,9 @@ pub(crate) struct InvalidBooleanValue {
2121
value: String,
2222
}
2323

24-
impl Display for InvalidBooleanValue {
25-
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
26-
write!(f, "{} was not a valid boolean", self.value)
24+
impl fmt::Display for InvalidBooleanValue {
25+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26+
write!(f, "{} is not a valid boolean", self.value)
2727
}
2828
}
2929

@@ -40,3 +40,26 @@ pub(crate) fn parse_bool(value: &str) -> Result<bool, InvalidBooleanValue> {
4040
})
4141
}
4242
}
43+
44+
#[derive(Debug)]
45+
pub(crate) struct InvalidUrlValue {
46+
value: String,
47+
}
48+
49+
impl fmt::Display for InvalidUrlValue {
50+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51+
write!(f, "{} is not a valid URL", self.value)
52+
}
53+
}
54+
55+
impl Error for InvalidUrlValue {}
56+
57+
pub(crate) fn parse_url(value: &str) -> Result<String, InvalidUrlValue> {
58+
match url::Url::parse(value) {
59+
// We discard the parse result because it includes a trailing slash
60+
Ok(_) => Ok(value.to_string()),
61+
Err(_) => Err(InvalidUrlValue {
62+
value: value.to_string(),
63+
}),
64+
}
65+
}

0 commit comments

Comments
 (0)