Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minimal Support for Shared Config Loading #675

Merged
merged 13 commits into from
Aug 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
vNext (Month Day, Year)
-----------------------
**Breaking Changes**
- `<sevicename>::from_env()` has been removed (#675). A drop-in replacement is available:
1. Add a dependency on `aws-config`:
```toml
[dependencies]
aws-config = { git = "https://github.com/awslabs/aws-sdk-rust", tag = "v0.0.17-alpha" }
```
2. Update your client creation code:
```rust
let client = <service>>::Client::new(&aws_config::load_from_env().await)
```

- `ProvideRegion` has been moved to `aws_config::meta::region::ProvideRegion`. (#675)
- `aws_types::region::ChainProvider` has been moved to `aws_config::meta::region::RegionProviderChain` (#675).
- `ProvideRegion` is now asynchronous. Code that called `provider.region()` must be changed to `provider.region().await`.
- `<awsservice>::Config::from_env()` is now also asynchronous because it must load a region
- `<awsservice>::Config::builder()` will **not** load a default region unspecified. A region must be specified directly.
- `<awsservice>::Config::builder()` will **not** load a default region. To preserve previous behavior:
1. Add a dependency on `aws-config`:
```toml
[dependencies]
aws-config = { git = "https://github.com/awslabs/aws-sdk-rust", tag = "v0.0.17-alpha" }
```
2. ```rust
let shared_config = aws_config::load_from_env().await;
let config = <service>::config::Builder::from(&shared_config).<other builder modifications>.build();
```
- `Request` and `Response` in `smithy_http::operation` now use `SharedPropertyBag` instead of `Arc<Mutex<PropertyBag>>`. Use the `acquire` and `acquire_mut` methods to get a reference to the underlying `PropertyBag` to access properties. (#667)

**New this week**
Expand Down
2 changes: 1 addition & 1 deletion aws/rust-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ members = [
"aws-sigv4"
]

exclude = ["aws-auth-providers"]
exclude = ["aws-auth-providers", "aws-config"]
1 change: 1 addition & 0 deletions aws/rust-runtime/aws-auth-providers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ default = ["rustls", "rt-tokio"]

[dependencies]
aws-auth = { path = "../../sdk/build/aws-sdk/aws-auth" }
aws-config = { path = "../../sdk/build/aws-sdk/aws-config"}
aws-types = { path = "../../sdk/build/aws-sdk/aws-types" }
aws-sdk-sts = { path = "../../sdk/build/aws-sdk/sts"}
aws-hyper = { path = "../../sdk/build/aws-sdk/aws-hyper"}
Expand Down
6 changes: 4 additions & 2 deletions aws/rust-runtime/aws-auth-providers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ fn default_connector() -> Option<DynConnector> {
/// This provider chain will use defaults for all settings. The region will be resolved with the default
/// provider chain. To construct a custom provider, use [`default_provider_chain::Builder`](default_provider_chain::Builder).
pub async fn default_provider() -> impl AsyncProvideCredentials {
use aws_types::region::ProvideRegion;
let resolved_region = aws_types::region::default_provider().region().await;
use aws_config::meta::region::ProvideRegion;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be worth it to duplicate the region method directly on the chain provider so that this import isn't necessary?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for backwards compatibility reasons this doesn't return chain_provider it returns impl ProvideRegion

let resolved_region = aws_config::default_provider::region::default_provider()
.region()
.await;
let mut builder = default_provider_chain::Builder::default();
builder.set_region(resolved_region);
builder.build()
Expand Down
2 changes: 1 addition & 1 deletion aws/rust-runtime/aws-auth-providers/src/profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ use std::sync::Arc;

use aws_auth::provider::env::EnvironmentVariableCredentialsProvider;
use aws_auth::provider::{AsyncProvideCredentials, BoxFuture, CredentialsError, CredentialsResult};
use aws_config::meta::region::ProvideRegion;
use aws_hyper::DynConnector;
use aws_sdk_sts::Region;
use aws_types::os_shim_internal::{Env, Fs};
use aws_types::profile::ProfileParseError;
use aws_types::region::ProvideRegion;
use tracing::Instrument;

use crate::must_have_connector;
Expand Down
27 changes: 27 additions & 0 deletions aws/rust-runtime/aws-config/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "aws-config"
version = "0.1.0"
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>", "Russell Cohen <rcoh@amazon.com>"]
edition = "2018"
exclude = ["test-data/*"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default-provider = ["profile", "imds", "meta", "sts"]
profile = ["sts", "web-identity-token"]
# note: IMDS currently unsupported
imds = []
meta = []
sts = []
web-identity-token = ["sts"]
sso = []

default = ["default-provider"]

[dependencies]
aws-types = { path = "../../sdk/build/aws-sdk/aws-types" }
smithy-async = { path = "../../sdk/build/aws-sdk/smithy-async" }
tracing = { version = "0.1" }

[dev-dependencies]
futures-util = "0.3.16"
20 changes: 20 additions & 0 deletions aws/rust-runtime/aws-config/src/default_provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

//! Default Provider chains for [`region`](default_provider::region) and credentials (TODO)

pub mod region {
//! Default region provider chain

use crate::environment::region::EnvironmentVariableRegionProvider;
use crate::meta::region::ProvideRegion;

/// Default Region Provider chain
///
/// This provider will load region from environment variables.
pub fn default_provider() -> impl ProvideRegion {
EnvironmentVariableRegionProvider::new()
}
}
9 changes: 9 additions & 0 deletions aws/rust-runtime/aws-config/src/environment/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

// TODO:
// pub mod credentials;
/// Load regions from the environment
pub mod region;
90 changes: 90 additions & 0 deletions aws/rust-runtime/aws-config/src/environment/region.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

use crate::meta::region::{future, ProvideRegion};
use aws_types::os_shim_internal::Env;
use aws_types::region::Region;

/// Load a region from environment variables
///
/// This provider will first check the value of `AWS_REGION`, falling back to `AWS_DEFAULT_REGION`
/// when `AWS_REGION` is unset.
#[derive(Debug, Default)]
pub struct EnvironmentVariableRegionProvider {
env: Env,
}

impl EnvironmentVariableRegionProvider {
/// Create a new `EnvironmentVariableRegionProvider`
pub fn new() -> Self {
EnvironmentVariableRegionProvider { env: Env::real() }
}

#[doc(hidden)]
/// Create an region provider from a given `Env`
///
/// This method is used for tests that need to override environment variables.
pub fn new_with_env(env: Env) -> Self {
EnvironmentVariableRegionProvider { env }
}
}

impl ProvideRegion for EnvironmentVariableRegionProvider {
fn region(&self) -> future::ProvideRegion {
let region = self
.env
.get("AWS_REGION")
.or_else(|_| self.env.get("AWS_DEFAULT_REGION"))
.map(Region::new)
.ok();
future::ProvideRegion::ready(region)
}
}
#[cfg(test)]
mod test {
use crate::environment::region::EnvironmentVariableRegionProvider;
use crate::meta::region::ProvideRegion;
use aws_types::os_shim_internal::Env;
use aws_types::region::Region;
use futures_util::FutureExt;

fn test_provider(vars: &[(&str, &str)]) -> EnvironmentVariableRegionProvider {
EnvironmentVariableRegionProvider::new_with_env(Env::from_slice(vars))
}

#[test]
fn no_region() {
assert_eq!(
test_provider(&[])
.region()
.now_or_never()
.expect("no polling"),
None
);
}

#[test]
fn prioritize_aws_region() {
let provider = test_provider(&[
("AWS_REGION", "us-east-1"),
("AWS_DEFAULT_REGION", "us-east-2"),
]);
assert_eq!(
provider.region().now_or_never().expect("no polling"),
Some(Region::new("us-east-1"))
);
}

#[test]
fn fallback_to_default_region() {
assert_eq!(
test_provider(&[("AWS_DEFAULT_REGION", "us-east-2")])
.region()
.now_or_never()
.expect("no polling"),
Some(Region::new("us-east-2"))
);
}
}
129 changes: 129 additions & 0 deletions aws/rust-runtime/aws-config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#![deny(missing_docs)]

//! `aws-config` provides implementations of region, credential (todo), and connector (todo) resolution.
//!
//! These implementations can be used either adhoc or via [`from_env`](from_env)/[`ConfigLoader`](ConfigLoader).
//! [`ConfigLoader`](ConfigLoader) can combine different configuration sources into an AWS shared-config:
//! [`Config`](aws_types::config::Config). [`Config`](aws_types::config::Config) can be used configure
//! an AWS service client.
//!
//! ## Examples
//! Load default SDK configuration:
//! ```rust
//! # mod aws_sdk_dynamodb {
//! # pub struct Client;
//! # impl Client {
//! # pub fn new(config: &aws_types::config::Config) -> Self { Client }
//! # }
//! # }
//! # async fn docs() {
//! let config = aws_config::load_from_env().await;
//! let client = aws_sdk_dynamodb::Client::new(&config);
//! # }
//! ```
//!
//! Load SDK configuration with a region override:
//! ```rust
//! use aws_config::meta::region::RegionProviderChain;
//! # mod aws_sdk_dynamodb {
//! # pub struct Client;
//! # impl Client {
//! # pub fn new(config: &aws_types::config::Config) -> Self { Client }
//! # }
//! # }
//! # async fn docs() {
//! let region_provider = RegionProviderChain::default_provider().or_else("us-east-1");
//! let config = aws_config::from_env().region(region_provider).load().await;
//! let client = aws_sdk_dynamodb::Client::new(&config);
//! # }
//! ```

/// Providers that implement the default AWS provider chain
#[cfg(feature = "default-provider")]
pub mod default_provider;

/// Providers that load configuration from environment variables
pub mod environment;

/// Meta-Providers that combine multiple providers into a single provider
#[cfg(feature = "meta")]
pub mod meta;

/// Create an environment loader for AWS Configuration
///
/// ## Example
/// ```rust
/// # async fn create_config() {
/// use aws_types::region::Region;
/// let config = aws_config::from_env().region("us-east-1").load().await;
/// # }
/// ```
#[cfg(feature = "default-provider")]
pub fn from_env() -> ConfigLoader {
ConfigLoader::default()
}

/// Load a default configuration from the environment
///
/// Convenience wrapper equivalent to `aws_config::from_env().load().await`
#[cfg(feature = "default-provider")]
pub async fn load_from_env() -> aws_types::config::Config {
from_env().load().await
}

#[cfg(feature = "default-provider")]
/// Load default sources for all configuration with override support
pub use loader::ConfigLoader;

mod loader {
use crate::default_provider::region;
use crate::meta::region::ProvideRegion;
use aws_types::config::Config;

/// Load a cross-service [`Config`](aws_types::config::Config) from the environment
///
/// This builder supports overriding individual components of the generated config. Overriding a component
/// will skip the standard resolution chain from **for that component**. For example,
/// if you override the region provider, _even if that provider returns None_, the default region provider
/// chain will not be used.
#[derive(Default, Debug)]
pub struct ConfigLoader {
region: Option<Box<dyn ProvideRegion>>,
}

impl ConfigLoader {
/// Override the region used to construct the [`Config`](aws_types::config::Config).
///
/// ## Example
/// ```rust
/// # async fn create_config() {
/// use aws_types::region::Region;
/// let config = aws_config::from_env()
/// .region(Region::new("us-east-1"))
/// .load().await;
/// # }
/// ```
pub fn region(mut self, region: impl ProvideRegion + 'static) -> Self {
self.region = Some(Box::new(region));
self
}

/// Load the default configuration chain
///
/// If fields have been overridden during builder construction, the override values will be used.
///
/// Otherwise, the default values for each field will be provided.
///
/// NOTE: When an override is provided, the default implementation is **not** used as a fallback.
/// This means that if you provide a region provider that does not return a region, no region will
/// be set in the resulting [`Config`](aws_types::config::Config)
pub async fn load(self) -> aws_types::config::Config {
let region = if let Some(provider) = self.region {
provider.region().await
} else {
region::default_provider().region().await
};
Config::builder().region(region).build()
}
}
}
12 changes: 12 additions & 0 deletions aws/rust-runtime/aws-config/src/meta/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/

/// Region Providers
pub mod region;

// coming soon:
// pub mod credentials:
// - CredentialProviderChain
// - LazyCachingProvider
Loading