Skip to content

Commit 9944b7a

Browse files
authored
Add caching library README and associated constructors (#13)
*Issue #, if available:* *Description of changes:* By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 61ee44e commit 9944b7a

File tree

6 files changed

+275
-2
lines changed

6 files changed

+275
-2
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ fabric.properties
6262

6363
# Visual Studio Code
6464
.vscode/*
65-
!.vscode/settings.json
6665
!.vscode/tasks.json
6766
!.vscode/launch.json
6867
!.vscode/extensions.json

Cargo.lock

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

aws_secretsmanager_caching/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ serde = { version = "1", features = ["derive"] }
1717
thiserror = "1"
1818
tokio = { version = "1", features = ["rt", "sync"] }
1919
linked-hash-map = "0.5.6"
20+
aws-config = "1"
2021

2122
[dev-dependencies]
2223
aws-smithy-mocks-experimental = "0"
2324
aws-smithy-runtime = { version = "1", features = ["test-util"] }
2425
aws-sdk-secretsmanager = { version = "1", features = ["test-util"] }
25-
tokio = { version = "1", features = ["macros", "rt", "sync"] }
26+
tokio = { version = "1", features = ["macros", "rt", "sync", "test-util"] }
2627
http = "0"
28+
tokio-test = "0.4.4"

aws_secretsmanager_caching/README.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# AWS Secrets Manager Rust Caching Client
2+
3+
The AWS Secrets Manager Rust Caching Client enables in-process caching of secrets for Rust applications.
4+
5+
## Getting Started
6+
7+
### Required Prerequisites
8+
9+
To use this client you must have:
10+
11+
* A Rust 2021 development environment. If you do not have one, go to [Rust Getting Started](https://www.rust-lang.org/learn/get-started) on the Rust Programming Language website, then download and install Rust.
12+
* An Amazon Web Services (AWS) account to access secrets stored in AWS Secrets Manager.
13+
* **To create an AWS account**, go to [Sign In or Create an AWS Account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) and then choose **I am a new user.** Follow the instructions to create an AWS account.
14+
* **To create a secret in AWS Secrets Manager**, go to [Creating Secrets](https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_create-basic-secret.html) and follow the instructions on that page.
15+
16+
### Get Started
17+
18+
The following code sample demonstrates how to get started:
19+
20+
1. Instantiate the caching client.
21+
2. Request secret.
22+
23+
```sh
24+
cargo add tokio -F rt-multi-thread,net,macros
25+
cargo add aws_secretsmanager_caching
26+
```
27+
28+
```rust
29+
use aws_secretsmanager_caching::SecretsManagerCachingClient;
30+
use std::num::NonZeroUsize;
31+
use std::time::Duration;
32+
33+
let client = match SecretsManagerCachingClient::default(
34+
NonZeroUsize::new(1000).unwrap(),
35+
Duration::from_secs(300),
36+
)
37+
.await
38+
{
39+
Ok(c) => c,
40+
Err(_) => panic!("Handle this error"),
41+
};
42+
43+
let secret_string = match client.get_secret_value("MyTest", None, None).await {
44+
Ok(s) => s.secret_string.unwrap(),
45+
Err(_) => panic!("Handle this error"),
46+
};
47+
48+
// Your code here
49+
```
50+
51+
### Cache Configuration
52+
53+
* `max_size: NonZeroUsize`: The maximum number of cached secrets to maintain before evicting secrets that have not been accessed recently.
54+
* `ttl: Duration`: The duration a cached item is considered valid before requiring a refresh of the secret state.
55+
56+
### Instantiating Cache with a custom Config and a custom Client
57+
58+
```sh
59+
cargo add aws_sdk_secretsmanager aws_config
60+
```
61+
62+
```rust
63+
let config = aws_config::load_defaults(BehaviorVersion::latest())
64+
.await
65+
.into_builder()
66+
.region(Region::from_static("us-west-2"))
67+
.build();
68+
69+
let asm_builder = aws_sdk_secretsmanager::config::Builder::from(&config);
70+
71+
let client = match SecretsManagerCachingClient::from_builder(
72+
asm_builder,
73+
NonZeroUsize::new(1000).unwrap(),
74+
Duration::from_secs(300),
75+
)
76+
.await
77+
{
78+
Ok(c) => c,
79+
Err(_) => panic!("Handle this error"),
80+
};
81+
82+
let secret_string = client
83+
.get_secret_value("MyTest", None, None)
84+
.await
85+
{
86+
Ok(c) => c.secret_string.unwrap(),
87+
Err(_) => panic!("Handle this error"),
88+
};
89+
90+
// Your code here
91+
```
92+
93+
### Getting Help
94+
95+
Please use these community resources for getting help:
96+
97+
* Ask a question on [Stack Overflow](https://stackoverflow.com/) and tag it with [aws-secrets-manager](https://stackoverflow.com/questions/tagged/aws-secrets-manager).
98+
* Open a support ticket with [AWS Support](https://console.aws.amazon.com/support/home#/)
99+
* If it turns out that you may have found a bug, or have a feature request, please [open an issue](https://github.com/aws/aws-secretsmanager-agent/issues/new/choose).

aws_secretsmanager_caching/src/lib.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@
1111
pub mod output;
1212
/// Manages the lifecycle of cached secrets
1313
pub mod secret_store;
14+
mod utils;
1415

16+
use aws_config::BehaviorVersion;
1517
use aws_sdk_secretsmanager::Client as SecretsManagerClient;
1618
use secret_store::SecretStoreError;
1719

1820
use output::GetSecretValueOutputDef;
1921
use secret_store::{MemoryStore, SecretStore};
2022
use std::{error::Error, num::NonZeroUsize, time::Duration};
2123
use tokio::sync::RwLock;
24+
use utils::CachingLibraryInterceptor;
2225

2326
/// AWS Secrets Manager Caching client
2427
#[derive(Debug)]
@@ -37,6 +40,24 @@ impl SecretsManagerCachingClient {
3740
/// * `asm_client` - Initialized AWS SDK Secrets Manager client instance
3841
/// * `max_size` - Maximum size of the store.
3942
/// * `ttl` - Time-to-live of the secrets in the store.
43+
/// ```rust
44+
/// use aws_sdk_secretsmanager::Client as SecretsManagerClient;
45+
/// use aws_sdk_secretsmanager::{config::Region, Config};
46+
/// use aws_secretsmanager_caching::SecretsManagerCachingClient;
47+
/// use std::num::NonZeroUsize;
48+
/// use std::time::Duration;
49+
50+
/// let asm_client = SecretsManagerClient::from_conf(
51+
/// Config::builder()
52+
/// .behavior_version_latest()
53+
/// .build(),
54+
/// );
55+
/// let client = SecretsManagerCachingClient::new(
56+
/// asm_client,
57+
/// NonZeroUsize::new(1000).unwrap(),
58+
/// Duration::from_secs(300),
59+
/// );
60+
/// ```
4061
pub fn new(
4162
asm_client: SecretsManagerClient,
4263
max_size: NonZeroUsize,
@@ -48,6 +69,75 @@ impl SecretsManagerCachingClient {
4869
})
4970
}
5071

72+
/// Create a new caching client with in-memory store and the default AWS SDK client configuration
73+
///
74+
/// # Arguments
75+
///
76+
/// * `max_size` - Maximum size of the store.
77+
/// * `ttl` - Time-to-live of the secrets in the store.
78+
/// ```rust
79+
/// tokio_test::block_on(async {
80+
/// use aws_secretsmanager_caching::SecretsManagerCachingClient;
81+
/// use std::num::NonZeroUsize;
82+
/// use std::time::Duration;
83+
///
84+
/// let client = SecretsManagerCachingClient::default(
85+
/// NonZeroUsize::new(1000).unwrap(),
86+
/// Duration::from_secs(300),
87+
/// ).await.unwrap();
88+
/// })
89+
/// ```
90+
pub async fn default(max_size: NonZeroUsize, ttl: Duration) -> Result<Self, SecretStoreError> {
91+
let default_config = &aws_config::load_defaults(BehaviorVersion::latest()).await;
92+
let asm_builder = aws_sdk_secretsmanager::config::Builder::from(default_config)
93+
.interceptor(CachingLibraryInterceptor);
94+
95+
let asm_client = SecretsManagerClient::from_conf(asm_builder.build());
96+
Self::new(asm_client, max_size, ttl)
97+
}
98+
99+
/// Create a new caching client with in-memory store from an AWS SDK client builder
100+
///
101+
/// # Arguments
102+
///
103+
/// * `asm_builder` - AWS Secrets Manager SDK client builder.
104+
/// * `max_size` - Maximum size of the store.
105+
/// * `ttl` - Time-to-live of the secrets in the store.
106+
///
107+
/// ```rust
108+
/// tokio_test::block_on(async {
109+
/// use aws_secretsmanager_caching::SecretsManagerCachingClient;
110+
/// use std::num::NonZeroUsize;
111+
/// use std::time::Duration;
112+
/// use aws_config::{BehaviorVersion, Region};
113+
114+
/// let config = aws_config::load_defaults(BehaviorVersion::latest())
115+
/// .await
116+
/// .into_builder()
117+
/// .region(Region::from_static("us-west-2"))
118+
/// .build();
119+
120+
/// let asm_builder = aws_sdk_secretsmanager::config::Builder::from(&config);
121+
122+
/// let client = SecretsManagerCachingClient::from_builder(
123+
/// asm_builder,
124+
/// NonZeroUsize::new(1000).unwrap(),
125+
/// Duration::from_secs(300),
126+
/// )
127+
/// .await.unwrap();
128+
/// })
129+
/// ```
130+
pub async fn from_builder(
131+
asm_builder: aws_sdk_secretsmanager::config::Builder,
132+
max_size: NonZeroUsize,
133+
ttl: Duration,
134+
) -> Result<Self, SecretStoreError> {
135+
let asm_client = SecretsManagerClient::from_conf(
136+
asm_builder.interceptor(CachingLibraryInterceptor).build(),
137+
);
138+
Self::new(asm_client, max_size, ttl)
139+
}
140+
51141
/// Retrieves the value of the secret from the specified version.
52142
///
53143
/// # Arguments
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use aws_sdk_secretsmanager::config::{
2+
interceptors::BeforeTransmitInterceptorContextMut, Intercept, RuntimeComponents,
3+
};
4+
use aws_smithy_types::config_bag::ConfigBag;
5+
6+
/// SDK interceptor to append the agent name and version to the User-Agent header for CloudTrail records.
7+
#[derive(Debug)]
8+
pub(crate) struct CachingLibraryInterceptor;
9+
10+
/// SDK interceptor to append the agent name and version to the User-Agent header for CloudTrail records.
11+
///
12+
/// This interceptor adds the agent name and version to the User-Agent header
13+
/// of outbound Secrets Manager SDK requests.
14+
impl Intercept for CachingLibraryInterceptor {
15+
fn name(&self) -> &'static str {
16+
"CachingLibraryInterceptor"
17+
}
18+
19+
fn modify_before_signing(
20+
&self,
21+
context: &mut BeforeTransmitInterceptorContextMut<'_>,
22+
_runtime_components: &RuntimeComponents,
23+
_cfg: &mut ConfigBag,
24+
) -> Result<(), aws_sdk_secretsmanager::error::BoxError> {
25+
let request = context.request_mut();
26+
let agent = request.headers().get("user-agent").unwrap_or_default(); // Get current agent
27+
let full_agent = format!(
28+
"{agent} AWSSecretsManagerCachingRust/{}",
29+
option_env!("CARGO_PKG_VERSION").unwrap_or("0.0.0")
30+
);
31+
request.headers_mut().insert("user-agent", full_agent); // Overwrite header.
32+
33+
Ok(())
34+
}
35+
}

0 commit comments

Comments
 (0)