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

Rust Wrapper for CRT endpoint resolver #317

Merged
merged 6 commits into from
Jul 6, 2023

Conversation

sauraank
Copy link
Contributor

@sauraank sauraank commented Jun 27, 2023

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).

@sauraank sauraank temporarily deployed to PR integration tests June 27, 2023 09:46 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 27, 2023 09:46 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 27, 2023 09:46 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 27, 2023 09:46 — with GitHub Actions Inactive
@sauraank sauraank self-assigned this Jun 27, 2023
@sauraank sauraank marked this pull request as draft June 27, 2023 10:28
@sauraank sauraank temporarily deployed to PR integration tests June 27, 2023 11:10 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 27, 2023 11:10 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 27, 2023 11:10 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 27, 2023 11:10 — with GitHub Actions Inactive
@sauraank sauraank marked this pull request as ready for review June 27, 2023 12:05
Copy link
Contributor

@dannycjones dannycjones left a 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

mountpoint-s3-crt/src/s3/endpoint_resolver.rs Show resolved Hide resolved
mountpoint-s3-crt/src/s3/endpoint_resolver.rs Outdated Show resolved Hide resolved
mountpoint-s3-crt/src/s3/endpoint_resolver.rs Outdated Show resolved Hide resolved
mountpoint-s3-crt/src/s3/endpoint_resolver.rs Outdated Show resolved Hide resolved
mountpoint-s3-crt/src/s3/endpoint_resolver.rs Outdated Show resolved Hide resolved
mountpoint-s3-crt/src/s3/endpoint_resolver.rs Outdated Show resolved Hide resolved
mountpoint-s3-crt/src/s3/endpoint_resolver.rs Outdated Show resolved Hide resolved
mountpoint-s3-crt/src/s3/endpoint_resolver.rs Outdated Show resolved Hide resolved
@sauraank sauraank temporarily deployed to PR integration tests June 28, 2023 11:05 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 28, 2023 11:05 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 28, 2023 11:05 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 28, 2023 11:05 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 28, 2023 11:07 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 28, 2023 11:07 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 28, 2023 11:07 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 28, 2023 11:07 — with GitHub Actions Inactive
@sauraank sauraank had a problem deploying to PR integration tests June 29, 2023 16:43 — with GitHub Actions Failure
@sauraank sauraank had a problem deploying to PR integration tests June 29, 2023 16:43 — with GitHub Actions Failure
@sauraank sauraank had a problem deploying to PR integration tests June 29, 2023 16:43 — with GitHub Actions Failure
@sauraank sauraank had a problem deploying to PR integration tests June 29, 2023 16:43 — with GitHub Actions Failure
@sauraank sauraank force-pushed the endpointResolverWrapper branch from 38a497c to 5267aa4 Compare June 30, 2023 10:54
@sauraank sauraank had a problem deploying to PR integration tests June 30, 2023 10:54 — with GitHub Actions Failure
@sauraank sauraank temporarily deployed to PR integration tests June 30, 2023 10:54 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 30, 2023 10:54 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests June 30, 2023 10:54 — with GitHub Actions Inactive
@sauraank sauraank requested a review from passaro July 5, 2023 09:28
passaro
passaro previously requested changes Jul 5, 2023
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))
Copy link
Contributor

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(..)

Copy link
Contributor Author

@sauraank sauraank Jul 5, 2023

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?

Copy link
Contributor

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.

addressing_style: AddressingStyle,
allocator: &Allocator,
) -> Result<Self, EndpointError> {
let endpoint_rule_engine = RuleEngine::new(allocator).unwrap();
Copy link
Contributor

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 unwraps. 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();
Copy link
Contributor

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?

Copy link
Contributor Author

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));
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Member

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_cursors into OsStrs, which you'll need to tweak slightly because you want to return an owned string here, not a reference.

Copy link
Member

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()?;
Copy link
Contributor

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() {
Copy link
Contributor

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"),
Copy link
Contributor

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"),
Copy link
Contributor

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"

@@ -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();
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Comment on lines 35 to 37
let resolved_endpoint = endpoint_rule_engine
.resolve(endpoint_request_context)
.map_err(|_| EndpointError::UnsupportedRegion(region.to_owned()))?;
Copy link
Contributor

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

pub fn from_region(
region: &str,
addressing_style: AddressingStyle,
allocator: &Allocator,
Copy link
Member

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.

Comment on lines 35 to 37
let resolved_endpoint = endpoint_rule_engine
.resolve(endpoint_request_context)
.map_err(|_| EndpointError::UnsupportedRegion(region.to_owned()))?;
Copy link
Member

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.

UnsupportedRegion(String),
#[error("Endpoint could not be resolved")]
UnresolvedEndpoint(#[from] mountpoint_s3_crt::common::error::Error),
Copy link
Member

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));
Copy link
Member

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_cursors into OsStrs, 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> {
Copy link
Member

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));
Copy link
Member

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.

@sauraank sauraank temporarily deployed to PR integration tests July 6, 2023 14:11 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests July 6, 2023 14:11 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests July 6, 2023 14:11 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests July 6, 2023 14:11 — with GitHub Actions Inactive
Signed-off-by: Ankit Saurabh <[email protected]>
@sauraank sauraank had a problem deploying to PR integration tests July 6, 2023 15:15 — with GitHub Actions Failure
@sauraank sauraank had a problem deploying to PR integration tests July 6, 2023 15:16 — with GitHub Actions Failure
@sauraank sauraank had a problem deploying to PR integration tests July 6, 2023 15:16 — with GitHub Actions Failure
@sauraank sauraank had a problem deploying to PR integration tests July 6, 2023 15:16 — with GitHub Actions Failure
Signed-off-by: Ankit Saurabh <[email protected]>
@sauraank sauraank temporarily deployed to PR integration tests July 6, 2023 15:55 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests July 6, 2023 15:55 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests July 6, 2023 15:55 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests July 6, 2023 15:55 — with GitHub Actions Inactive
UnsupportedRegion(String),
#[error("Endpoint could not be resolved")]
UnresolvedEndpoint(#[from] mountpoint_s3_crt::common::error::Error),
#[error("Endpoint could not be resolved. {0}")]
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
#[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}")]
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
#[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()
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.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")]
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
#[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")]
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
#[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]>
@sauraank sauraank temporarily deployed to PR integration tests July 6, 2023 19:05 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests July 6, 2023 19:05 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests July 6, 2023 19:05 — with GitHub Actions Inactive
@sauraank sauraank temporarily deployed to PR integration tests July 6, 2023 19:05 — with GitHub Actions Inactive
@sauraank
Copy link
Contributor Author

sauraank commented Jul 6, 2023

Thanks @jamesbornholt @passaro for the review. Addressed all the recommendations.

Copy link
Member

@jamesbornholt jamesbornholt left a 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.

@jamesbornholt jamesbornholt merged commit 2d69769 into awslabs:main Jul 6, 2023
@sauraank sauraank deleted the endpointResolverWrapper branch September 1, 2023 13:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants