Skip to content

Commit 3df1cb9

Browse files
author
Anish Cheraku
committed
address code review feedback
1 parent 8127ec1 commit 3df1cb9

File tree

4 files changed

+268
-148
lines changed

4 files changed

+268
-148
lines changed

sources/Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sources/api/apiclient/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ fips = ["tls", "aws-lc-rs/fips", "rustls/fips"]
1616

1717
[dependencies]
1818
async-trait.workspace = true
19-
aws-smithy-types.workspace = true
2019
aws-config.workspace = true
2120
aws-lc-rs = { workspace = true, optional = true, features = ["bindgen"] }
2221
aws-sdk-s3.workspace = true
@@ -37,19 +36,21 @@ log.workspace = true
3736
models.workspace = true
3837
nix.workspace = true
3938
rand = { workspace = true, features = ["default"] }
40-
reqwest.workspace = true
39+
reqwest.workspace = true
4140
retry-read.workspace = true
4241
rustls = { workspace = true, optional = true }
4342
serde = { workspace = true, features = ["derive"] }
4443
serde_json.workspace = true
4544
signal-hook.workspace = true
4645
simplelog.workspace = true
4746
snafu = { workspace = true, features = ["futures"] }
47+
test-case = "*"
4848
tokio = { workspace = true, features = ["fs", "io-std", "io-util", "macros", "rt-multi-thread", "time"] }
4949
tokio-tungstenite = { workspace = true, features = ["connect"] }
5050
toml.workspace = true
5151
unindent.workspace = true
5252
url.workspace = true
53+
aws-smithy-runtime-api.workspace = true
5354

5455
[build-dependencies]
5556
generate-readme.workspace = true

sources/api/apiclient/src/apply.rs

Lines changed: 46 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
//! This module allows application of settings from URIs or stdin. The inputs are expected to be
22
//! TOML settings files, in the same format as user data, or the JSON equivalent. The inputs are
33
//! pulled and applied to the API server in a single transaction.
4-
//! use aws_smithy_runtime_api::client::result::SdkError;
5-
use aws_sdk_secretsmanager::operation::get_secret_value::GetSecretValueError;
6-
use aws_sdk_secretsmanager::config::http::HttpResponse as SdkHttpResponse;
7-
use aws_sdk_ssm::operation::get_parameter::GetParameterError;
8-
use crate::uri_resolver::{StdinUri, FileUri, HttpUri, S3Uri, UriResolver, SecretsManagerUri, SsmUri};
4+
use crate::apply::error::ResolverFailureSnafu;
95
use crate::rando;
10-
use futures::future::{join, ready, TryFutureExt};
6+
use futures::future::{join, ready};
117
use futures::stream::{self, StreamExt};
128
use reqwest::Url;
139
use serde::de::{Deserialize, IntoDeserializer};
14-
use snafu::{futures::try_future::TryFutureExt as SnafuTryFutureExt, OptionExt, ResultExt};
10+
use snafu::{OptionExt, ResultExt};
1511
use std::convert::TryFrom;
1612
use std::path::Path;
17-
use tokio::io::AsyncReadExt;
18-
1913

2014
/// Reads settings in TOML or JSON format from files at the requested URIs (or from stdin, if given
2115
/// "-"), then commits them in a single transaction and applies them to the system.
@@ -72,49 +66,65 @@ where
7266
Ok(())
7367
}
7468

69+
/// Holds the raw input string and (if it parses) the URL.
70+
pub struct SettingsInput {
71+
pub input: String,
72+
pub parsed_url: Option<Url>,
73+
}
74+
impl SettingsInput {
75+
pub(crate) fn new(input: impl Into<String>) -> Self {
76+
let input = input.into();
77+
let parsed_url = Url::parse(&input).ok();
78+
SettingsInput { input, parsed_url }
79+
}
80+
}
81+
7582
/// Retrieves the given source location and returns the result in a String.
76-
pub async fn get(input: &str) -> Result<String> {
77-
let resolver = select_resolver(input)?;
78-
resolver.resolve().await
83+
async fn get<S>(input_source: S) -> Result<String>
84+
where
85+
S: AsRef<str>,
86+
{
87+
let settings = SettingsInput::new(input_source.as_ref());
88+
let resolver = select_resolver(&settings)?;
89+
resolver.resolve().await.context(ResolverFailureSnafu)
7990
}
8091

81-
/// Choose which UriResolver applies to `input` (stdin, file://, http(s):// or s3://).
82-
fn select_resolver(input: &str) -> Result<Box<dyn UriResolver>> {
83-
// 1) "-" → stdin
84-
if let Ok(r) = StdinUri::try_from(input) {
92+
/// Choose which UriResolver applies to `input` (stdin, file://, http(s)://, s3://, secretsmanager://, and ssm://).
93+
fn select_resolver(input: &SettingsInput) -> Result<Box<dyn crate::uri_resolver::UriResolver>> {
94+
use crate::uri_resolver;
95+
96+
// stdin ("-")
97+
if let Ok(r) = uri_resolver::StdinUri::try_from(input) {
8598
return Ok(Box::new(r));
8699
}
87100

88-
// 2) parse as a URL
89-
let url = Url::parse(input).context(error::UriSnafu { input_source: input.to_string() })?;
90-
91-
// 3) file://
92-
if let Ok(r) = FileUri::try_from(&url) {
101+
// file://
102+
if let Ok(r) = uri_resolver::FileUri::try_from(input) {
93103
return Ok(Box::new(r));
94104
}
95105

96-
// 4) http(s)://
97-
if let Ok(r) = HttpUri::try_from(url.clone()) {
106+
// http(s)://
107+
if let Ok(r) = uri_resolver::HttpUri::try_from(input) {
98108
return Ok(Box::new(r));
99109
}
100110

101-
// 5) s3://
102-
if let Ok(r) = S3Uri::try_from(url.clone()) {
111+
// s3://
112+
if let Ok(r) = uri_resolver::S3Uri::try_from(input) {
103113
return Ok(Box::new(r));
104114
}
105115

106-
// 6) secretsmanager://
107-
if let Ok(r) = SecretsManagerUri::try_from(input) {
116+
// secretsmanager://
117+
if let Ok(r) = uri_resolver::SecretsManagerUri::try_from(input) {
108118
return Ok(Box::new(r));
109119
}
110120

111-
// 6) ssm://
112-
if let Ok(r) = SsmUri::try_from(input) {
121+
// ssm://
122+
if let Ok(r) = uri_resolver::SsmUri::try_from(input) {
113123
return Ok(Box::new(r));
114124
}
115125

116126
error::NoResolverSnafu {
117-
input_source: input.to_string(),
127+
input_source: input.input.clone(),
118128
}
119129
.fail()
120130
}
@@ -153,8 +163,6 @@ fn format_change(input: &str, input_source: &str) -> Result<String> {
153163
}
154164

155165
pub(crate) mod error {
156-
use aws_sdk_secretsmanager::operation::get_secret_value::GetSecretValueError;
157-
use aws_sdk_ssm::operation::get_parameter::GetParameterError;
158166
use snafu::Snafu;
159167

160168
#[derive(Debug, Snafu)]
@@ -168,15 +176,6 @@ pub(crate) mod error {
168176
source: Box<crate::Error>,
169177
},
170178

171-
#[snafu(display("Failed to read given file '{}': {}", input_source, source))]
172-
FileRead {
173-
input_source: String,
174-
source: std::io::Error,
175-
},
176-
177-
#[snafu(display("Given invalid file URI '{}'", input_source))]
178-
FileUri { input_source: String },
179-
180179
#[snafu(display("No URI resolver found for '{}'", input_source))]
181180
NoResolver { input_source: String },
182181

@@ -234,45 +233,9 @@ pub(crate) mod error {
234233
source: reqwest::Error,
235234
},
236235

237-
#[snafu(display("Invalid S3 URI '{}': missing bucket name", input_source))]
238-
S3UriMissingBucket { input_source: String },
239-
240236
#[snafu(display("Given invalid file URI '{}'", input_source))]
241237
InvalidFileUri { input_source: String },
242238

243-
#[snafu(display("Given HTTP(S) URI '{}'", input_source))]
244-
InvalidHTTPUri { input_source: String },
245-
246-
#[snafu(display("Failed to read standard input: {}", source))]
247-
StdinRead { source: std::io::Error },
248-
249-
#[snafu(display("Invalid S3 URI scheme for '{}', expected s3://", input_source))]
250-
S3UriScheme { input_source: String },
251-
252-
#[snafu(display("Invalid Secrets Manager URI scheme for '{}', expected secretsmanager://", input_source))]
253-
SecretsManagerUri { input_source: String },
254-
255-
#[snafu(display("Invalid SSM URI scheme for '{}', expected ssm://", input_source))]
256-
SsmUri { input_source: String },
257-
258-
#[snafu(display("Failed to fetch secret '{}' from Secrets Manager: {}", secret_id, source))]
259-
SecretsManagerGet {
260-
secret_id: String,
261-
source: aws_sdk_secretsmanager::error::SdkError<GetSecretValueError>,
262-
},
263-
264-
#[snafu(display("Secrets Manager secret '{}' did not return a string value", secret_id))]
265-
SecretsManagerStringMissing { secret_id: String },
266-
267-
#[snafu(display("Failed to fetch parameter '{}' from SSM: {}", parameter_name, source))]
268-
SsmGetParameter {
269-
parameter_name: String,
270-
source: aws_sdk_ssm::error::SdkError<GetParameterError>,
271-
},
272-
273-
#[snafu(display("SSM parameter '{}' did not return a string value", parameter_name))]
274-
SsmParameterMissing { parameter_name: String },
275-
276239
#[snafu(display(
277240
"Failed to translate TOML from '{}' to JSON for API: {}",
278241
input_source,
@@ -288,7 +251,13 @@ pub(crate) mod error {
288251
input_source: String,
289252
source: url::ParseError,
290253
},
254+
255+
#[snafu(display("Resolver failed: {}", source))]
256+
ResolverFailure { source: crate::uri_resolver::ResolverError },
257+
291258
}
259+
292260
}
293261
pub use error::Error;
294262
pub type Result<T> = std::result::Result<T, error::Error>;
263+

0 commit comments

Comments
 (0)