Skip to content

Commit

Permalink
Added support for Amazon OpenSearch Serverless.
Browse files Browse the repository at this point in the history
Signed-off-by: dblock <dblock@amazon.com>
  • Loading branch information
dblock committed Jan 31, 2023
1 parent b863472 commit 073874d
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Adds Github workflow for changelog verification ([#89](https://github.com/opensearch-project/opensearch-rs/pull/89))
- Adds Github workflow for unit tests ([#112](https://github.com/opensearch-project/opensearch-rs/pull/112))
- Updates users guide ([#114](https://github.com/opensearch-project/opensearch-rs/pull/114))
- Adds support for OpenSearch Serverless ([#96](https://github.com/opensearch-project/opensearch-rs/pull/96))

### Dependencies
- Bumps `simple_logger` from 2.3.0 to 4.0.0
Expand Down
8 changes: 5 additions & 3 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
- [Add a Document to the Index](#add-a-document-to-the-index)
- [Search for a Document](#search-for-a-document)
- [Delete the Index](#delete-the-index)
- [Amazon OpenSearch Service](#amazon-opensearch-service)
- [Amazon OpenSearch and OpenSearch Serverless](#amazon-opensearch-and-opensearch-serverless)
- [Create a Client](#create-a-client-1)

# User Guide
Expand Down Expand Up @@ -103,21 +103,23 @@ client
.await?;
```

## Amazon OpenSearch Service
## Amazon OpenSearch and OpenSearch Serverless

This library supports [Amazon OpenSearch Service](https://aws.amazon.com/opensearch-service/).
This library supports [Amazon OpenSearch Service](https://aws.amazon.com/opensearch-service/) and [OpenSearch Serverless](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/serverless.html).

### Create a Client

Create a client with AWS credentials as follows. Make sure to specify the correct service name and signing region.

```rust
let url = Url::parse("https://...");
let service_name = "es"; // use "aoss" for OpenSearch Serverless
let conn_pool = SingleNodeConnectionPool::new(url?);
let region_provider = RegionProviderChain::default_provider().or_else("us-east-1");
let aws_config = aws_config::from_env().region(region_provider).load().await.clone();
let transport = TransportBuilder::new(conn_pool)
.auth(aws_config.clone().try_into()?)
.service_name(service_name)
.build()?;
let client = OpenSearch::new(transport);
```
16 changes: 10 additions & 6 deletions opensearch/src/http/aws_auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,28 @@ use aws_credential_types::{
provider::{ProvideCredentials, SharedCredentialsProvider}
};
use aws_sigv4::{
http_request::{sign, SignableBody, SignableRequest, SigningParams, SigningSettings},
http_request::{sign, SignableBody, SignableRequest, SigningParams, SigningSettings, PayloadChecksumKind},
signing_params::BuildError,
};
use aws_types::region::Region;
use reqwest::Request;

const SERVICE_NAME: &str = "es";

fn get_signing_params<'a>(
credentials: &'a Credentials,
service_name: &'a str,
region: &'a Region,
) -> Result<SigningParams<'a>, BuildError> {

let mut signing_settings = SigningSettings::default();
signing_settings.payload_checksum_kind = PayloadChecksumKind::XAmzSha256; // required for OpenSearch Serverless

let mut builder = SigningParams::builder()
.access_key(credentials.access_key_id())
.secret_key(credentials.secret_access_key())
.service_name(SERVICE_NAME)
.service_name(service_name)
.region(region.as_ref())
.time(SystemTime::now())
.settings(SigningSettings::default());
.settings(signing_settings);

builder.set_security_token(credentials.session_token());

Expand All @@ -44,11 +47,12 @@ fn get_signing_params<'a>(
pub async fn sign_request(
request: &mut Request,
credentials_provider: &SharedCredentialsProvider,
service_name: &str,
region: &Region,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let credentials = credentials_provider.provide_credentials().await?;

let params = get_signing_params(&credentials, region)?;
let params = get_signing_params(&credentials, service_name, region)?;

let uri = request.url().as_str().parse()?;

Expand Down
19 changes: 18 additions & 1 deletion opensearch/src/http/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ pub struct TransportBuilder {
disable_proxy: bool,
headers: HeaderMap,
timeout: Option<Duration>,
#[cfg(feature = "aws-auth")]
service_name: String,
}

impl TransportBuilder {
Expand All @@ -174,6 +176,8 @@ impl TransportBuilder {
disable_proxy: false,
headers: HeaderMap::new(),
timeout: None,
#[cfg(feature = "aws-auth")]
service_name: "es".to_string()
}
}

Expand Down Expand Up @@ -241,6 +245,15 @@ impl TransportBuilder {
self
}

/// Sets a global AWS service name.
///
/// Default is "es". Other supported services are "aoss" for OpenSearch Serverless.
#[cfg(feature = "aws-auth")]
pub fn service_name(mut self, service_name: &str) -> Self {
self.service_name = service_name.to_string();
self
}

/// Builds a [Transport] to use to send API calls to Elasticsearch.
pub fn build(self) -> Result<Transport, BuildError> {
let mut client_builder = self.client_builder;
Expand Down Expand Up @@ -313,6 +326,8 @@ impl TransportBuilder {
client,
conn_pool: self.conn_pool,
credentials: self.credentials,
#[cfg(feature = "aws-auth")]
service_name: self.service_name,
})
}
}
Expand Down Expand Up @@ -352,6 +367,8 @@ pub struct Transport {
client: reqwest::Client,
credentials: Option<Credentials>,
conn_pool: Box<dyn ConnectionPool>,
#[cfg(feature = "aws-auth")]
service_name: String
}

impl Transport {
Expand Down Expand Up @@ -460,7 +477,7 @@ impl Transport {

#[cfg(feature = "aws-auth")]
if let Some(Credentials::AwsSigV4(credentials_provider, region)) = &self.credentials {
super::aws_auth::sign_request(&mut request, credentials_provider, region)
super::aws_auth::sign_request(&mut request, credentials_provider, &self.service_name, region)
.await
.map_err(|e| crate::error::lib(format!("AWSV4 Signing Failed: {}", e)))?;
}
Expand Down
11 changes: 6 additions & 5 deletions opensearch/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
//! - **experimental-apis**: Enables experimental APIs. Experimental APIs are just that - an experiment. An experimental
//! API might have breaking changes in any future version, or it might even be removed entirely. This feature also
//! enables `beta-apis`.
//! - **aws-auth**: Enables authentication with Amazon OpenSearch.
//! - **aws-auth**: Enables authentication with Amazon OpenSearch and OpenSearch Serverless.
//! Performs AWS SigV4 signing using credential types from `aws-types`.
//!
//! # Getting started
Expand Down Expand Up @@ -327,9 +327,9 @@
//! # }
//! ```
//!
//! ## Amazon OpenSearch
//! ## Amazon OpenSearch and OpenSearch Serverless
//!
//! For authenticating against an Amazon OpenSearch endpoint using AWS SigV4 request signing,
//! For authenticating against an Amazon OpenSearch or OpenSearch Serverless endpoint using AWS SigV4 request signing,
//! you must enable the `aws-auth` feature, then pass the AWS credentials to the [TransportBuilder](http::transport::TransportBuilder).
//! The easiest way to retrieve AWS credentials in the required format is to use [aws-config](https://docs.rs/aws-config/latest/aws_config/).
//!
Expand All @@ -349,14 +349,15 @@
//! # use std::convert::TryInto;
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # #[cfg(feature = "aws-auth")] {
//! let creds = aws_config::load_from_env().await;
//! let url = Url::parse("https://example.com")?;
//! let url = Url::parse("https://...")?;
//! let region_provider = RegionProviderChain::default_provider().or_else("us-east-1");
//! let aws_config = aws_config::from_env().region(region_provider).load().await.clone();
//! let conn_pool = SingleNodeConnectionPool::new(url);
//! # #[cfg(feature = "aws-auth")] {
//! let transport = TransportBuilder::new(conn_pool)
//! .auth(aws_config.clone().try_into()?)
//! .service_name("es") // use "aoss" for OpenSearch Serverless
//! .build()?;
//! let client = OpenSearch::new(transport);
//! # }
Expand Down
2 changes: 1 addition & 1 deletion yaml_test_runner/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ serde_yaml = "0.9"
serde_json = { version = "~1", features = ["arbitrary_precision"] }
simple_logger = "4.0.0"
syn = { version = "~1.0", features = ["full"] }
sysinfo = "0.26.4"
sysinfo = "0.26"
url = "2.1.1"
yaml-rust = "0.4.3"
tar = "~0.4"
Expand Down

0 comments on commit 073874d

Please sign in to comment.