From d38a4665530b86eb6d1edad466e70fdd65a6245c Mon Sep 17 00:00:00 2001 From: Vacha Shah Date: Sat, 21 Jan 2023 22:20:58 -0800 Subject: [PATCH] Adding support and examples for AOSS (#216) * Adding x-amzn-content-sha256 header for signed requests Signed-off-by: Vacha Shah * Adding examples in user guide Signed-off-by: Vacha Shah * Adding CHANGELOG entry Signed-off-by: Vacha Shah * Addressing comments Signed-off-by: Vacha Shah * Fixing user guide Signed-off-by: Vacha Shah Signed-off-by: Vacha Shah --- CHANGELOG.md | 1 + USER_GUIDE.md | 79 +++++++++++++++++++++++--------- signer/awsv2/sdkv2signer.go | 1 + signer/awsv2/sdkv2signer_test.go | 3 ++ 4 files changed, 62 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aabd2ccf..163f453b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Added - Github workflow for changelog verification ([#172](https://github.com/opensearch-project/opensearch-go/pull/172)) - Add Go Documentation link for the client ([#182](https://github.com/opensearch-project/opensearch-go/pull/182)) +- Support for Amazon OpenSearch Serverless ([#216](https://github.com/opensearch-project/opensearch-go/pull/216)) ### Dependencies - Bumps `github.com/stretchr/testify` from 1.8.0 to 1.8.1 diff --git a/USER_GUIDE.md b/USER_GUIDE.md index 727e0ebd4..7cf07b49b 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -1,6 +1,8 @@ - [User Guide](#user-guide) - - [Example](#example) - - [How to use IAMs as authentication method](#how-to-use-iams-as-authentication-method) + - [Example](#example) + - [Amazon OpenSearch Service](#amazon-opensearch-service) + - [AWS SDK V1](#aws-sdk-v1) + - [AWS SDK V2](#aws-sdk-v2) # User Guide @@ -136,7 +138,7 @@ func main() { ``` -## How to use IAMs as authentication method +## Amazon OpenSearch Service Before starting, we strongly recommend reading the full AWS documentation regarding using IAM credentials to sign requests to OpenSearch APIs. @@ -221,32 +223,39 @@ func main() { #### AWS SDK V2 +Use the AWS SDK v2 for Go to authenticate with Amazon OpenSearch service. + ```go package main import ( "context" - "io" "log" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" - "github.com/opensearch-project/opensearch-go/v2/opensearchapi" + opensearch "github.com/opensearch-project/opensearch-go/v2" + opensearchapi "github.com/opensearch-project/opensearch-go/v2/opensearchapi" requestsigner "github.com/opensearch-project/opensearch-go/v2/signer/awsv2" ) -const endpoint = "" // e.g. https://opensearch-domain.region.com +const endpoint = "" // e.g. https://opensearch-domain.region.com or Amazon OpenSearch Serverless endpoint func main() { ctx := context.Background() - cfg, err := config.LoadDefaultConfig(ctx) + awsCfg, err := config.LoadDefaultConfig(ctx, + config.WithRegion(""), + config.WithCredentialsProvider( + getCredentialProvider("", "", ""), + ), + ) if err != nil { log.Fatal(err) // Do not log.fatal in a production ready app. } // Create an AWS request Signer and load AWS configuration using default config folder or env vars. - // See https://docs.aws.amazon.com/opensearch-service/latest/developerguide/request-signing.html#request-signing-go - signer, err := requestsigner.NewSigner(cfg) + signer, err := requestsigner.NewSignerWithService(awsCfg, "es") // "aoss" for Amazon OpenSearch Serverless if err != nil { log.Fatal(err) // Do not log.fatal in a production ready app. } @@ -260,26 +269,52 @@ func main() { log.Fatal("client creation err", err) } - ping := opensearchapi.PingRequest{} + indexName = "go-test-index" - resp, err := ping.Do(ctx, client) + // Define index mapping. + mapping := strings.NewReader(`{ + "settings": { + "index": { + "number_of_shards": 4 + } + } + }`) + + // Create an index with non-default settings. + createIndex := opensearchapi.IndicesCreateRequest{ + Index: indexName, + Body: mapping, + } + createIndexResponse, err := createIndex.Do(context.Background(), client) if err != nil { - log.Fatal(err) + log.Println("Error ", err.Error()) + log.Println("failed to create index ", err) + log.Fatal("create response body read err", err) } - defer resp.Body.Close() + log.Println(createIndexResponse) - if resp.IsError() { - log.Println("ping response status ", resp.Status()) - - respBody, err := io.ReadAll(resp.Body) - if err != nil { - log.Fatal("response body read err", err) - } + // Delete previously created index. + deleteIndex := opensearchapi.IndicesDeleteRequest{ + Index: []string{indexName}, + } - log.Fatal("ping resp body", respBody) + deleteIndexResponse, err := deleteIndex.Do(context.Background(), client) + if err != nil { + log.Println("failed to delete index ", err) + log.Fatal("delete index response body read err", err) } + log.Println("deleting index", deleteIndexResponse) +} - log.Println("PING OK") +func getCredentialProvider(accessKey, secretAccessKey, token string) aws.CredentialsProviderFunc { + return func(ctx context.Context) (aws.Credentials, error) { + c := &aws.Credentials{ + AccessKeyID: accessKey, + SecretAccessKey: secretAccessKey, + SessionToken: token, + } + return *c, nil + } } ``` diff --git a/signer/awsv2/sdkv2signer.go b/signer/awsv2/sdkv2signer.go index 3d44a17a9..1312fae4b 100644 --- a/signer/awsv2/sdkv2signer.go +++ b/signer/awsv2/sdkv2signer.go @@ -68,6 +68,7 @@ func (s *awsSdkV2Signer) SignRequest(r *http.Request) error { } hash, err := hexEncodedSha256OfRequest(r) + r.Header.Set("X-Amz-Content-Sha256", hash) if err != nil { return err } diff --git a/signer/awsv2/sdkv2signer_test.go b/signer/awsv2/sdkv2signer_test.go index 95d1f24d7..fcc0ce365 100644 --- a/signer/awsv2/sdkv2signer_test.go +++ b/signer/awsv2/sdkv2signer_test.go @@ -81,6 +81,7 @@ func TestV4SignerAwsSdkV2(t *testing.T) { q := req.Header assert.NotEmpty(t, q.Get("Authorization")) assert.NotEmpty(t, q.Get("X-Amz-Date")) + assert.NotEmpty(t, q.Get("X-Amz-Content-Sha256")) }) t.Run("sign request success with body", func(t *testing.T) { req, err := http.NewRequest( @@ -109,6 +110,7 @@ func TestV4SignerAwsSdkV2(t *testing.T) { q := req.Header assert.NotEmpty(t, q.Get("Authorization")) assert.NotEmpty(t, q.Get("X-Amz-Date")) + assert.NotEmpty(t, q.Get("X-Amz-Content-Sha256")) }) t.Run("sign request success with body for other AWS Services", func(t *testing.T) { @@ -139,6 +141,7 @@ func TestV4SignerAwsSdkV2(t *testing.T) { q := req.Header assert.NotEmpty(t, q.Get("Authorization")) assert.NotEmpty(t, q.Get("X-Amz-Date")) + assert.NotEmpty(t, q.Get("X-Amz-Content-Sha256")) }) t.Run("sign request failed due to invalid service", func(t *testing.T) {