-
Notifications
You must be signed in to change notification settings - Fork 174
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
Rust Wrapper for CRT endpoint resolver #317
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't get a chance to review fully, dropping some comments here
38a497c
to
5267aa4
Compare
mountpoint-s3-client/src/endpoint.rs
Outdated
let endpoint_rule_engine = RuleEngine::new(allocator).unwrap(); | ||
let mut endpoint_request_context = RequestContext::new(allocator).unwrap(); | ||
endpoint_request_context | ||
.add_string(allocator, OsStr::new("Region"), OsStr::new(region)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you could use .as_ref()
instead of OsStr::new(..)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Does it provide any difference that I should be aware of?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, new
will just call as_ref
.
mountpoint-s3-client/src/endpoint.rs
Outdated
addressing_style: AddressingStyle, | ||
allocator: &Allocator, | ||
) -> Result<Self, EndpointError> { | ||
let endpoint_rule_engine = RuleEngine::new(allocator).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little confused by all the unwrap
s. What are the error conditions here?
.ok_or_last_error()?; | ||
match aws_endpoints_resolved_endpoint_get_type(out_endpoint) { | ||
aws_endpoints_resolved_endpoint_type::AWS_ENDPOINTS_RESOLVED_ERROR => { | ||
let mut out_error: aws_byte_cursor = Default::default(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this error string ever used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I wanted to use this error string to get the error. But there is no method for CRT error to get the error other than from int.
match aws_endpoints_resolved_endpoint_get_type(out_endpoint) { | ||
aws_endpoints_resolved_endpoint_type::AWS_ENDPOINTS_RESOLVED_ERROR => { | ||
let mut out_error: aws_byte_cursor = Default::default(); | ||
let err = Error::from(aws_endpoints_resolved_endpoint_get_error(out_endpoint, &mut out_error)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe aws_endpoints_resolved_endpoint_get_error
is guaranteed to succeed (in this branch), so aren't you always return success here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to get error out of out_error
which this is not doing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah,aws_endpoints_resolved_endpoint_get_error
always returns AWS_OP_SUCCESS here. In other words, the return value of aws_endpoints_resolved_endpoint_get_error
is telling you whether that operation (get_error
) succeeded or not, which it always will in this branch.
This is why we were talking about making the return type of this method Result<Result<ResolvedEndpoint, String>, Error>
. The outer Result
captures "internal" errors, like the JSON not parsing or the rules being invalid. The inner Result
captures the effect of the actual resolver logic, which sometimes gives an endpoint but sometimes gives an error string.
The Uri
struct has a bunch of examples of turning aws_byte_cursor
s into OsStr
s, which you'll need to tweak slightly because you want to return an owned string here, not a reference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this case is leaking the aws_endpoints_resolved_endpoint
right now; it needs a release
.
Err(err) | ||
} | ||
aws_endpoints_resolved_endpoint_type::AWS_ENDPOINTS_RESOLVED_ENDPOINT => { | ||
let out_endpoint = out_endpoint.ok_or_last_error()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is last_error
here? How is it different from line 42?
} | ||
|
||
#[test] | ||
fn test_force_path_style_setting() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has this been added?
.add_string( | ||
&new_allocator, | ||
OsStr::new("Bucket"), | ||
OsStr::new("mountpoint-o-o0f129d3877cfede5ed9w5rxpf30v7bhaa83yause10--op-s3"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use an obviously fabricated string, e.g. "mountpoint-o-o000s3-bucket-test0000000000000000000000000--op-s3"
.add_string( | ||
&new_allocator, | ||
OsStr::new("Bucket"), | ||
OsStr::new("arn:aws:s3::accountID:accesspoint/mfzwi23gnjvgw.mrap"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, maybe something like: "arn:aws:s3::accountID:accesspoint/s3-bucket-test.mrap"
mountpoint-s3/src/main.rs
Outdated
@@ -480,7 +481,7 @@ fn create_client_for_bucket( | |||
addressing_style: AddressingStyle, | |||
) -> Result<S3CrtClient, anyhow::Error> { | |||
const DEFAULT_REGION: &str = "us-east-1"; | |||
|
|||
let allocator = Allocator::default(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this really necessary? Couldn't from_region
just use the default allocator? I don't think we want to expose this outside the client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. It can. In fact, I was making that change in my work for ARN support yesterday. Changing it here as well.
mountpoint-s3-client/src/endpoint.rs
Outdated
let resolved_endpoint = endpoint_rule_engine | ||
.resolve(endpoint_request_context) | ||
.map_err(|_| EndpointError::UnsupportedRegion(region.to_owned()))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 on this: I do not understand the difference between UnresolvedEndpoint
and UnsupportedRegion
mountpoint-s3-client/src/endpoint.rs
Outdated
pub fn from_region( | ||
region: &str, | ||
addressing_style: AddressingStyle, | ||
allocator: &Allocator, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't think this needs an allocator argument, it should just use Allocator::default()
like S3CrtClient::new()
does.
mountpoint-s3-client/src/endpoint.rs
Outdated
let resolved_endpoint = endpoint_rule_engine | ||
.resolve(endpoint_request_context) | ||
.map_err(|_| EndpointError::UnsupportedRegion(region.to_owned()))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think UnsupportedRegion
probably just shouldn't exist, unless we feel like we need to distinguish it from other errors somewhere? In general, we're not going to be able to map every possible error from the endpoint resolver -- there are too many.
mountpoint-s3-client/src/endpoint.rs
Outdated
UnsupportedRegion(String), | ||
#[error("Endpoint could not be resolved")] | ||
UnresolvedEndpoint(#[from] mountpoint_s3_crt::common::error::Error), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should also include the error string we got back from the resolver, which will explain why we couldn't resolve an endpoint.
match aws_endpoints_resolved_endpoint_get_type(out_endpoint) { | ||
aws_endpoints_resolved_endpoint_type::AWS_ENDPOINTS_RESOLVED_ERROR => { | ||
let mut out_error: aws_byte_cursor = Default::default(); | ||
let err = Error::from(aws_endpoints_resolved_endpoint_get_error(out_endpoint, &mut out_error)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah,aws_endpoints_resolved_endpoint_get_error
always returns AWS_OP_SUCCESS here. In other words, the return value of aws_endpoints_resolved_endpoint_get_error
is telling you whether that operation (get_error
) succeeded or not, which it always will in this branch.
This is why we were talking about making the return type of this method Result<Result<ResolvedEndpoint, String>, Error>
. The outer Result
captures "internal" errors, like the JSON not parsing or the rules being invalid. The inner Result
captures the effect of the actual resolver logic, which sometimes gives an endpoint but sometimes gives an error string.
The Uri
struct has a bunch of examples of turning aws_byte_cursor
s into OsStr
s, which you'll need to tweak slightly because you want to return an owned string here, not a reference.
} | ||
|
||
/// Add the parameter to [RequestContext] whose value is in form of string | ||
pub fn add_string(&mut self, allocator: &Allocator, name: &OsStr, value: &OsStr) -> Result<(), Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this is slightly easier to use if it's name: impl AsRef<OsStr>, value: impl AsRef<OsStr>
. Inside the method, use name.as_ref()
instead of just name
. Same thing for name
in add_boolean
. You can see examples in methods like Header::has_header
.
This way you don't have to do the OsStr::new(...)
dance when you try to call it.
match aws_endpoints_resolved_endpoint_get_type(out_endpoint) { | ||
aws_endpoints_resolved_endpoint_type::AWS_ENDPOINTS_RESOLVED_ERROR => { | ||
let mut out_error: aws_byte_cursor = Default::default(); | ||
let err = Error::from(aws_endpoints_resolved_endpoint_get_error(out_endpoint, &mut out_error)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this case is leaking the aws_endpoints_resolved_endpoint
right now; it needs a release
.
Signed-off-by: Ankit Saurabh <[email protected]>
Signed-off-by: Ankit Saurabh <[email protected]>
Signed-off-by: Ankit Saurabh <[email protected]>
Signed-off-by: Ankit Saurabh <[email protected]>
mountpoint-s3-client/src/endpoint.rs
Outdated
UnsupportedRegion(String), | ||
#[error("Endpoint could not be resolved")] | ||
UnresolvedEndpoint(#[from] mountpoint_s3_crt::common::error::Error), | ||
#[error("Endpoint could not be resolved. {0}")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[error("Endpoint could not be resolved. {0}")] | |
#[error("endpoint could not be resolved")] |
(no need to include the nested error when it's a #[from]
, it gets captured automatically
#[derive(Debug, Error, PartialEq, Eq)] | ||
pub enum ResolverError { | ||
/// The header was not found | ||
#[error("Resolved Endpoint Error: {0}")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[error("Resolved Endpoint Error: {0}")] | |
#[error("endpoint not resolved: {0}")] |
Err(err) | ||
aws_endpoints_resolved_endpoint_get_error(out_endpoint, &mut out_error); | ||
let resolved_endpoint_error = std::str::from_utf8(aws_byte_cursor_as_slice(&out_error)) | ||
.unwrap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.unwrap() | |
.expect("endpoint errors are always valid UTF-8") |
#[test_case("UseDualStack", "https://s3-bucket-test.s3.dualstack.us-east-1.amazonaws.com"; "Using Dual Stack (IPv6) option")] | ||
#[test_case("Accelerate", "https://s3-bucket-test.s3-accelerate.amazonaws.com"; "Using transfer acceleration option")] | ||
#[test_case("ForcePathStyle", "https://s3.us-east-1.amazonaws.com/s3-bucket-test"; "Addressing style set to Path style")] | ||
#[test_case("InvalidMountOption", "https://s3-bucket-test.s3.us-east-1.amazonaws.com"; "Invalid mount option is ignored")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[test_case("InvalidMountOption", "https://s3-bucket-test.s3.us-east-1.amazonaws.com"; "Invalid mount option is ignored")] | |
#[test_case("InvalidOption", "https://s3-bucket-test.s3.us-east-1.amazonaws.com"; "Invalid option is ignored")] |
#[test] | ||
fn test_fips_setting() { | ||
#[test_case("s3-bucket-test", "cn-north-1", "https://s3-bucket-test.s3.cn-north-1.amazonaws.com.cn"; "regions outside aws partition")] | ||
#[test_case("s3-bucket-test", "eu-west-1", "https://s3-bucket-test.s3.eu-west-1.amazonaws.com"; "regions within aws partition")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#[test_case("s3-bucket-test", "eu-west-1", "https://s3-bucket-test.s3.eu-west-1.amazonaws.com"; "regions within aws partition")] | |
#[test_case("s3-bucket-test", "eu-west-1", "https://s3-bucket-test.s3.eu-west-1.amazonaws.com"; "regions within aws partition")] | |
#[test_case("s3-bucket-test", "us-gov-west-1", "https://s3-bucket-test.s3.us-gov-west-1.amazonaws.com"; "regions in aws-us-gov partition")] |
Signed-off-by: Ankit Saurabh <[email protected]>
Thanks @jamesbornholt @passaro for the review. Addressed all the recommendations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Let's do the rest in a separate change, like you were suggesting.
Implementation of rust wrapper for endpoint resolver in CRT. Added a few tests as well. Used those rust wrappers in endpoint.rs to support regions outside aws region.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and I agree to the terms of the Developer Certificate of Origin (DCO).