From 58eef3bdb1d51deea672bf1497dae713aa673aad Mon Sep 17 00:00:00 2001 From: Yaron Schneider Date: Tue, 14 May 2024 08:38:54 -0700 Subject: [PATCH 1/4] Fix linter for cosmosdb (#3416) Signed-off-by: yaron2 --- state/azure/cosmosdb/cosmosdb.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/state/azure/cosmosdb/cosmosdb.go b/state/azure/cosmosdb/cosmosdb.go index deeeb2157b..72d812e721 100644 --- a/state/azure/cosmosdb/cosmosdb.go +++ b/state/azure/cosmosdb/cosmosdb.go @@ -688,7 +688,7 @@ func isNotFoundError(err error) bool { } if requestError, ok := err.(*azcore.ResponseError); ok { - if requestError.StatusCode == 404 { + if requestError.StatusCode == http.StatusNotFound { return true } // we previously checked the error code, but unfortunately this is not stable between API versions From 4e4aa26c9e575f7877cf10b3761d80ab6616c7c6 Mon Sep 17 00:00:00 2001 From: denisbchrsk <155584191+denisbchrsk@users.noreply.github.com> Date: Wed, 15 May 2024 17:23:33 +0300 Subject: [PATCH 2/4] Kafka PubSub: Propagate partition key to DLT (#3368) Signed-off-by: denisbchrsk <155584191+denisbchrsk@users.noreply.github.com> Co-authored-by: Yaron Schneider --- common/component/kafka/producer.go | 44 +++--- common/component/kafka/producer_test.go | 194 ++++++++++++++++++++++++ 2 files changed, 219 insertions(+), 19 deletions(-) create mode 100644 common/component/kafka/producer_test.go diff --git a/common/component/kafka/producer.go b/common/component/kafka/producer.go index 4c04400c57..9dd18fe8a5 100644 --- a/common/component/kafka/producer.go +++ b/common/component/kafka/producer.go @@ -16,6 +16,7 @@ package kafka import ( "context" "errors" + "maps" "github.com/IBM/sarama" @@ -58,17 +59,18 @@ func (k *Kafka) Publish(_ context.Context, topic string, data []byte, metadata m } for name, value := range metadata { - if name == key { + switch name { + case key, keyMetadataKey: msg.Key = sarama.StringEncoder(value) - } else { - if msg.Headers == nil { - msg.Headers = make([]sarama.RecordHeader, 0, len(metadata)) - } - msg.Headers = append(msg.Headers, sarama.RecordHeader{ - Key: []byte(name), - Value: []byte(value), - }) } + + if msg.Headers == nil { + msg.Headers = make([]sarama.RecordHeader, 0, len(metadata)) + } + msg.Headers = append(msg.Headers, sarama.RecordHeader{ + Key: []byte(name), + Value: []byte(value), + }) } partition, offset, err := k.producer.SendMessage(msg) @@ -109,19 +111,23 @@ func (k *Kafka) BulkPublish(_ context.Context, topic string, entries []pubsub.Bu // the metadata in that field is compared to the entry metadata to generate the right response on partial failures msg.Metadata = entry.EntryId - for name, value := range metadata { - if name == key { + maps.Copy(entry.Metadata, metadata) + + for name, value := range entry.Metadata { + switch name { + case key, keyMetadataKey: msg.Key = sarama.StringEncoder(value) - } else { - if msg.Headers == nil { - msg.Headers = make([]sarama.RecordHeader, 0, len(metadata)) - } - msg.Headers = append(msg.Headers, sarama.RecordHeader{ - Key: []byte(name), - Value: []byte(value), - }) } + + if msg.Headers == nil { + msg.Headers = make([]sarama.RecordHeader, 0, len(metadata)) + } + msg.Headers = append(msg.Headers, sarama.RecordHeader{ + Key: []byte(name), + Value: []byte(value), + }) } + msgs = append(msgs, msg) } diff --git a/common/component/kafka/producer_test.go b/common/component/kafka/producer_test.go new file mode 100644 index 0000000000..3dd1b75a9e --- /dev/null +++ b/common/component/kafka/producer_test.go @@ -0,0 +1,194 @@ +package kafka + +import ( + "context" + "testing" + + "github.com/IBM/sarama" + saramamocks "github.com/IBM/sarama/mocks" + "github.com/stretchr/testify/require" + + "github.com/dapr/components-contrib/pubsub" + "github.com/dapr/kit/logger" +) + +func arrangeKafkaWithAssertions(t *testing.T, msgCheckers ...saramamocks.MessageChecker) *Kafka { + cfg := saramamocks.NewTestConfig() + mockProducer := saramamocks.NewSyncProducer(t, cfg) + + for _, msgChecker := range msgCheckers { + mockProducer.ExpectSendMessageWithMessageCheckerFunctionAndSucceed(msgChecker) + } + + return &Kafka{ + producer: mockProducer, + config: cfg, + logger: logger.NewLogger("kafka_test"), + } +} + +func getSaramaHeadersFromMetadata(metadata map[string]string) []sarama.RecordHeader { + headers := make([]sarama.RecordHeader, 0, len(metadata)) + + for key, value := range metadata { + headers = append(headers, sarama.RecordHeader{ + Key: []byte(key), + Value: []byte(value), + }) + } + + return headers +} + +func createMessageAsserter(t *testing.T, expectedKey sarama.Encoder, expectedHeaders map[string]string) saramamocks.MessageChecker { + return func(msg *sarama.ProducerMessage) error { + require.Equal(t, expectedKey, msg.Key) + require.ElementsMatch(t, getSaramaHeadersFromMetadata(expectedHeaders), msg.Headers) + return nil + } +} + +func TestPublish(t *testing.T) { + ctx := context.Background() + + t.Run("produce message without partition key", func(t *testing.T) { + // arrange + metadata := map[string]string{ + "a": "a", + } + messageAsserter := createMessageAsserter(t, nil, metadata) + k := arrangeKafkaWithAssertions(t, messageAsserter) + + // act + err := k.Publish(ctx, "a", []byte("a"), metadata) + + // assert + require.NoError(t, err) + }) + + t.Run("produce message with partition key when partitionKey in metadata", func(t *testing.T) { + // arrange + metadata := map[string]string{ + "a": "a", + "partitionKey": "key", + } + messageAsserter := createMessageAsserter(t, sarama.StringEncoder("key"), metadata) + k := arrangeKafkaWithAssertions(t, messageAsserter) + + // act + err := k.Publish(ctx, "a", []byte("a"), metadata) + + // assert + require.NoError(t, err) + }) + + t.Run("produce message with partition key when __key in metadata", func(t *testing.T) { + // arrange + metadata := map[string]string{ + "a": "a", + "__key": "key", + } + messageAsserter := createMessageAsserter(t, sarama.StringEncoder("key"), metadata) + k := arrangeKafkaWithAssertions(t, messageAsserter) + + // act + err := k.Publish(ctx, "a", []byte("a"), metadata) + + // assert + require.NoError(t, err) + }) +} + +func TestBulkPublish(t *testing.T) { + ctx := context.Background() + metadata := map[string]string{ + "common": "common", + } + + t.Run("bulk produce messages without partition key", func(t *testing.T) { + // arrange + entries := []pubsub.BulkMessageEntry{ + { + EntryId: "0", + Event: []byte("a"), + ContentType: "a", + Metadata: map[string]string{"b": "b"}, + }, + { + EntryId: "0", + Event: []byte("a"), + ContentType: "a", + Metadata: map[string]string{"c": "c"}, + }, + } + messageAsserters := []saramamocks.MessageChecker{ + createMessageAsserter(t, nil, map[string]string{"b": "b", "common": "common"}), + createMessageAsserter(t, nil, map[string]string{"c": "c", "common": "common"}), + } + k := arrangeKafkaWithAssertions(t, messageAsserters...) + + // act + _, err := k.BulkPublish(ctx, "a", entries, metadata) + + // assert + require.NoError(t, err) + }) + + t.Run("bulk produce messages with partition key when partitionKey in entry metadata", func(t *testing.T) { + // arrange + entries := []pubsub.BulkMessageEntry{ + { + EntryId: "0", + Event: []byte("a"), + ContentType: "a", + Metadata: map[string]string{"partitionKey": "key"}, + }, + { + EntryId: "0", + Event: []byte("a"), + ContentType: "a", + Metadata: map[string]string{"c": "c"}, + }, + } + messageAsserters := []saramamocks.MessageChecker{ + createMessageAsserter(t, sarama.StringEncoder("key"), map[string]string{"partitionKey": "key", "common": "common"}), + createMessageAsserter(t, nil, map[string]string{"c": "c", "common": "common"}), + } + k := arrangeKafkaWithAssertions(t, messageAsserters...) + + // act + _, err := k.BulkPublish(ctx, "a", entries, metadata) + + // assert + require.NoError(t, err) + }) + + t.Run("bulk produce messages with partition key when __key in entry metadata", func(t *testing.T) { + // arrange + entries := []pubsub.BulkMessageEntry{ + { + EntryId: "0", + Event: []byte("a"), + ContentType: "a", + Metadata: map[string]string{"__key": "key"}, + }, + { + EntryId: "0", + Event: []byte("a"), + ContentType: "a", + Metadata: map[string]string{"c": "c"}, + }, + } + messageAsserters := []saramamocks.MessageChecker{ + createMessageAsserter(t, sarama.StringEncoder("key"), map[string]string{"__key": "key", "common": "common"}), + createMessageAsserter(t, nil, map[string]string{"c": "c", "common": "common"}), + } + k := arrangeKafkaWithAssertions(t, messageAsserters...) + + // act + _, err := k.BulkPublish(ctx, "a", entries, metadata) + + // assert + require.NoError(t, err) + }) +} From 70fd16ab19a600a3f982dbb20120566c5d23aca7 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 16 May 2024 14:58:15 -0500 Subject: [PATCH 3/4] fix: metadata capitalization (#3413) Signed-off-by: Bernd Verst Signed-off-by: Samantha Coyle Co-authored-by: Bernd Verst Co-authored-by: Yaron Schneider --- bindings/azure/eventhubs/metadata.yaml | 6 +++--- pubsub/azure/eventhubs/metadata.yaml | 2 +- pubsub/rabbitmq/rabbitmq.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bindings/azure/eventhubs/metadata.yaml b/bindings/azure/eventhubs/metadata.yaml index f6250ce69d..439fc5a9e6 100644 --- a/bindings/azure/eventhubs/metadata.yaml +++ b/bindings/azure/eventhubs/metadata.yaml @@ -90,8 +90,8 @@ builtinAuthenticationProfiles: entity management is enabled. metadata: # Input-only metadata - # consumerGroup is an alias for consumerId, if both are defined consumerId takes precedence. - - name: consumerId + # consumerGroup is an alias for consumerID, if both are defined consumerID takes precedence. + - name: consumerID type: string required: true # consumerGroup is an alias for this field, let's promote this to default binding: @@ -108,7 +108,7 @@ metadata: output: false description: | The name of the Event Hubs Consumer Group to listen on. - Alias to consumerId. + Alias to consumerID. example: '"group1"' deprecated: true - name: storageAccountKey diff --git a/pubsub/azure/eventhubs/metadata.yaml b/pubsub/azure/eventhubs/metadata.yaml index b9cd436f24..768d472252 100644 --- a/pubsub/azure/eventhubs/metadata.yaml +++ b/pubsub/azure/eventhubs/metadata.yaml @@ -97,7 +97,7 @@ metadata: description: | Storage container name. example: '"myeventhubstoragecontainer"' - - name: consumerId + - name: consumerID type: string required: true # consumerGroup is an alias for this field, let's promote this to default description: | diff --git a/pubsub/rabbitmq/rabbitmq.go b/pubsub/rabbitmq/rabbitmq.go index 63d6e080f7..854ba098c7 100644 --- a/pubsub/rabbitmq/rabbitmq.go +++ b/pubsub/rabbitmq/rabbitmq.go @@ -521,7 +521,7 @@ func (r *rabbitMQ) subscribeForever(ctx context.Context, req pubsub.SubscribeReq msgs, err = channel.Consume( q.Name, - queueName, // consumerId + queueName, // consumerID r.metadata.AutoAck, // autoAck false, // exclusive false, // noLocal From eb8229362317d0811d17d031a097438ab39758c2 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 16 May 2024 17:45:57 -0500 Subject: [PATCH 4/4] feat(aws iam): support aws iam auth for postgresql components (#3324) Signed-off-by: Samantha Coyle Signed-off-by: Bernd Verst Signed-off-by: joshvanl Co-authored-by: Alessandro (Ale) Segala <43508+ItalyPaleAle@users.noreply.github.com> Co-authored-by: Bernd Verst Co-authored-by: joshvanl --- .../builtin-authentication-profiles.yaml | 6 + .build-tools/go.mod | 2 +- .build-tools/go.sum | 4 +- bindings/kafka/metadata.yaml | 2 +- bindings/postgres/metadata.go | 19 +++- bindings/postgres/metadata.yaml | 40 +++++++ bindings/postgres/postgres.go | 4 +- common/authentication/aws/aws.go | 107 ++++++++++++++++++ common/authentication/postgresql/metadata.go | 71 ++++++++++-- common/component/kafka/kafka.go | 2 +- common/component/postgresql/v1/metadata.go | 7 +- .../component/postgresql/v1/metadata_test.go | 55 ++++++--- common/component/postgresql/v1/postgresql.go | 25 ++-- configuration/postgres/metadata.go | 22 +++- configuration/postgres/metadata.yaml | 40 +++++++ configuration/postgres/postgres.go | 16 ++- go.mod | 8 +- go.sum | 6 +- pubsub/kafka/metadata.yaml | 2 +- state/postgresql/v1/metadata.yaml | 43 +++++++ state/postgresql/v1/postgresql.go | 1 + state/postgresql/v2/metadata.go | 7 +- state/postgresql/v2/metadata.yaml | 45 +++++++- state/postgresql/v2/metadata_test.go | 57 +++++++--- state/postgresql/v2/postgresql.go | 43 +++++-- tests/certification/embedded/embedded.go | 6 +- tests/certification/go.mod | 19 ++-- tests/certification/go.sum | 52 +++++---- .../hashicorp/vault/metadata_helpers.go | 4 +- tests/conformance/state_test.go | 2 +- tests/e2e/pubsub/jetstream/go.mod | 2 +- tests/e2e/pubsub/jetstream/go.sum | 4 +- .../utils/configupdater/postgres/postgres.go | 14 ++- 33 files changed, 601 insertions(+), 136 deletions(-) diff --git a/.build-tools/builtin-authentication-profiles.yaml b/.build-tools/builtin-authentication-profiles.yaml index 0f545d356f..5c8557471e 100644 --- a/.build-tools/builtin-authentication-profiles.yaml +++ b/.build-tools/builtin-authentication-profiles.yaml @@ -3,6 +3,12 @@ aws: description: | Authenticate using an Access Key ID and Secret Access Key included in the metadata metadata: + - name: awsRegion + type: string + required: true + description: | + The AWS Region where the AWS Relational Database Service is deployed to. + example: '"us-east-1"' - name: accessKey description: AWS access key associated with an IAM account required: true diff --git a/.build-tools/go.mod b/.build-tools/go.mod index b218215b9c..a9a023d901 100644 --- a/.build-tools/go.mod +++ b/.build-tools/go.mod @@ -14,7 +14,7 @@ require ( ) require ( - github.com/dapr/kit v0.13.1-0.20240306152601-e33fbab74548 // indirect + github.com/dapr/kit v0.13.1-0.20240402103809-0c7cfce53d9e // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect diff --git a/.build-tools/go.sum b/.build-tools/go.sum index 06a3963c3c..9b08233839 100644 --- a/.build-tools/go.sum +++ b/.build-tools/go.sum @@ -1,6 +1,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/dapr/kit v0.13.1-0.20240306152601-e33fbab74548 h1:rh6cYZ/2nnufSAhnz+fwmsyNWljt0uyxmfIcG6t499I= -github.com/dapr/kit v0.13.1-0.20240306152601-e33fbab74548/go.mod h1:dons8V2bF6MPR2yFdxtTa86PfaE7EJtKAOkZ9hOavBQ= +github.com/dapr/kit v0.13.1-0.20240402103809-0c7cfce53d9e h1:mLvqfGuppb6uhsijmwTlF5sZVtGvig+Ua5ESKF17SxA= +github.com/dapr/kit v0.13.1-0.20240402103809-0c7cfce53d9e/go.mod h1:dons8V2bF6MPR2yFdxtTa86PfaE7EJtKAOkZ9hOavBQ= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/bindings/kafka/metadata.yaml b/bindings/kafka/metadata.yaml index 39112ce535..3b1a077a7b 100644 --- a/bindings/kafka/metadata.yaml +++ b/bindings/kafka/metadata.yaml @@ -174,7 +174,7 @@ authenticationProfiles: type: string sensitive: true description: | - AWS session token to use. A session token is only required if you are using\ntemporary security credentials. + AWS session token to use. A session token is only required if you are using temporary security credentials. example: '"TOKEN"' - name: awsIamRoleArn type: string diff --git a/bindings/postgres/metadata.go b/bindings/postgres/metadata.go index 379a17a43a..b4747c33ff 100644 --- a/bindings/postgres/metadata.go +++ b/bindings/postgres/metadata.go @@ -14,26 +14,41 @@ limitations under the License. package postgres import ( + "time" + + "github.com/dapr/components-contrib/common/authentication/aws" pgauth "github.com/dapr/components-contrib/common/authentication/postgresql" kitmd "github.com/dapr/kit/metadata" ) +const ( + defaultTimeout = 20 * time.Second // Default timeout for network requests +) + type psqlMetadata struct { pgauth.PostgresAuthMetadata `mapstructure:",squash"` + aws.AWSIAM `mapstructure:",squash"` + Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"` } func (m *psqlMetadata) InitWithMetadata(meta map[string]string) error { // Reset the object m.PostgresAuthMetadata.Reset() + m.Timeout = defaultTimeout err := kitmd.DecodeMetadata(meta, &m) if err != nil { return err } + opts := pgauth.InitWithMetadataOpts{ + AzureADEnabled: true, + AWSIAMEnabled: true, + } + // Validate and sanitize input - // Azure AD auth is supported for this component - err = m.PostgresAuthMetadata.InitWithMetadata(meta, true) + // Azure AD & AWS IAM auth is supported for this component + err = m.PostgresAuthMetadata.InitWithMetadata(meta, opts) if err != nil { return err } diff --git a/bindings/postgres/metadata.yaml b/bindings/postgres/metadata.yaml index 4832a14762..defbe66b8b 100644 --- a/bindings/postgres/metadata.yaml +++ b/bindings/postgres/metadata.yaml @@ -38,6 +38,40 @@ builtinAuthenticationProfiles: example: | "host=mydb.postgres.database.azure.com user=myapplication port=5432 database=dapr_test sslmode=require" type: string + - name: "aws" + metadata: + - name: useAWSIAM + required: true + type: bool + example: '"true"' + description: | + Must be set to `true` to enable the component to retrieve access tokens from AWS IAM. + This authentication method only works with AWS Relational Database Service for PostgreSQL databases. + - name: awsRegion + type: string + required: true + description: | + The AWS Region where the MSK Kafka broker is deployed to. + example: '"us-east-1"' + - name: awsAccessKey + type: string + required: true + description: | + AWS access key associated with an IAM account. + example: '"AKIAIOSFODNN7EXAMPLE"' + - name: awsSecretKey + type: string + required: true + sensitive: true + description: | + The secret key associated with the access key. + example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"' + - name: awsSessionToken + type: string + sensitive: true + description: | + AWS session token to use. A session token is only required if you are using temporary security credentials. + example: '"TOKEN"' authenticationProfiles: - title: "Connection string" description: "Authenticate using a Connection String" @@ -54,6 +88,12 @@ authenticationProfiles: or "postgres://dapr:secret@dapr.example.com:5432/dapr?sslmode=verify-ca" type: string metadata: + - name: timeout + required: false + description: Timeout for all database operations. + example: "30s" + default: "20s" + type: duration - name: maxConns required: false description: | diff --git a/bindings/postgres/postgres.go b/bindings/postgres/postgres.go index 3a0ab98252..38f528a6b4 100644 --- a/bindings/postgres/postgres.go +++ b/bindings/postgres/postgres.go @@ -66,9 +66,9 @@ func (p *Postgres) Init(ctx context.Context, meta bindings.Metadata) error { return err } - poolConfig, err := m.GetPgxPoolConfig() + poolConfig, err := m.GetPgxPoolConfig(ctx) if err != nil { - return fmt.Errorf("error opening DB connection: %w", err) + return err } // This context doesn't control the lifetime of the connection pool, and is diff --git a/common/authentication/aws/aws.go b/common/authentication/aws/aws.go index 2ecccc815a..7d88e27378 100644 --- a/common/authentication/aws/aws.go +++ b/common/authentication/aws/aws.go @@ -14,14 +14,29 @@ limitations under the License. package aws import ( + "context" + "errors" + "fmt" + "strconv" + "time" + + "github.com/aws/aws-sdk-go-v2/config" + v2creds "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/feature/rds/auth" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/session" + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgxpool" "github.com/dapr/kit/logger" ) +type EnvironmentSettings struct { + Metadata map[string]string +} + func GetClient(accessKey string, secretKey string, sessionToken string, region string, endpoint string) (*session.Session, error) { awsConfig := aws.NewConfig() @@ -53,3 +68,95 @@ func GetClient(accessKey string, secretKey string, sessionToken string, region s return awsSession, nil } + +// NewEnvironmentSettings returns a new EnvironmentSettings configured for a given AWS resource. +func NewEnvironmentSettings(md map[string]string) (EnvironmentSettings, error) { + es := EnvironmentSettings{ + Metadata: md, + } + + return es, nil +} + +type AWSIAM struct { + // Ignored by metadata parser because included in built-in authentication profile + // access key to use for accessing postgresql. + AWSAccessKey string `json:"awsAccessKey" mapstructure:"awsAccessKey"` + // secret key to use for accessing postgresql. + AWSSecretKey string `json:"awsSecretKey" mapstructure:"awsSecretKey"` + // aws session token to use. + AWSSessionToken string `mapstructure:"awsSessionToken"` + // aws region in which postgresql should create resources. + AWSRegion string `mapstructure:"awsRegion"` +} + +type AWSIAMAuthOptions struct { + PoolConfig *pgxpool.Config `json:"poolConfig" mapstructure:"poolConfig"` + ConnectionString string `json:"connectionString" mapstructure:"connectionString"` + Region string `json:"region" mapstructure:"region"` + AccessKey string `json:"accessKey" mapstructure:"accessKey"` + SecretKey string `json:"secretKey" mapstructure:"secretKey"` +} + +func (opts *AWSIAMAuthOptions) GetAccessToken(ctx context.Context) (string, error) { + dbEndpoint := opts.PoolConfig.ConnConfig.Host + ":" + strconv.Itoa(int(opts.PoolConfig.ConnConfig.Port)) + var authenticationToken string + + // https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.Connecting.Go.html + // Default to load default config through aws credentials file (~/.aws/credentials) + awsCfg, err := config.LoadDefaultConfig(ctx) + // Note: in the event of an error with invalid config or failed to load config, + // then we fall back to using the access key and secret key. + switch { + case errors.Is(err, config.SharedConfigAssumeRoleError{}.Err), + errors.Is(err, config.SharedConfigLoadError{}.Err), + errors.Is(err, config.SharedConfigProfileNotExistError{}.Err): + // Validate if access key and secret access key are provided + if opts.AccessKey == "" || opts.SecretKey == "" { + return "", fmt.Errorf("failed to load default configuration for AWS using accessKey and secretKey: %w", err) + } + + // Set credentials explicitly + awsCfg := v2creds.NewStaticCredentialsProvider(opts.AccessKey, opts.SecretKey, "") + authenticationToken, err = auth.BuildAuthToken( + ctx, dbEndpoint, opts.Region, opts.PoolConfig.ConnConfig.User, awsCfg) + if err != nil { + return "", fmt.Errorf("failed to create AWS authentication token: %w", err) + } + + return authenticationToken, nil + case err != nil: + return "", fmt.Errorf("failed to load default AWS authentication configuration") + } + + authenticationToken, err = auth.BuildAuthToken( + ctx, dbEndpoint, opts.Region, opts.PoolConfig.ConnConfig.User, awsCfg.Credentials) + if err != nil { + return "", fmt.Errorf("failed to create AWS authentication token: %w", err) + } + + return authenticationToken, nil +} + +func (opts *AWSIAMAuthOptions) InitiateAWSIAMAuth(ctx context.Context) error { + // Set max connection lifetime to 8 minutes in postgres connection pool configuration. + // Note: this will refresh connections before the 15 min expiration on the IAM AWS auth token, + // while leveraging the BeforeConnect hook to recreate the token in time dynamically. + opts.PoolConfig.MaxConnLifetime = time.Minute * 8 + + // Setup connection pool config needed for AWS IAM authentication + opts.PoolConfig.BeforeConnect = func(ctx context.Context, pgConfig *pgx.ConnConfig) error { + // Manually reset auth token with aws and reset the config password using the new iam token + pwd, errGetAccessToken := opts.GetAccessToken(ctx) + if errGetAccessToken != nil { + return fmt.Errorf("failed to refresh access token for iam authentication with PostgreSQL: %w", errGetAccessToken) + } + + pgConfig.Password = pwd + opts.PoolConfig.ConnConfig.Password = pwd + + return nil + } + + return nil +} diff --git a/common/authentication/postgresql/metadata.go b/common/authentication/postgresql/metadata.go index d8bf9c25c3..6f56b57b8f 100644 --- a/common/authentication/postgresql/metadata.go +++ b/common/authentication/postgresql/metadata.go @@ -23,7 +23,9 @@ import ( "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" + "github.com/dapr/components-contrib/common/authentication/aws" "github.com/dapr/components-contrib/common/authentication/azure" + "github.com/dapr/components-contrib/metadata" ) // PostgresAuthMetadata contains authentication metadata for PostgreSQL components. @@ -32,9 +34,11 @@ type PostgresAuthMetadata struct { ConnectionMaxIdleTime time.Duration `mapstructure:"connectionMaxIdleTime"` MaxConns int `mapstructure:"maxConns"` UseAzureAD bool `mapstructure:"useAzureAD"` + UseAWSIAM bool `mapstructure:"useAWSIAM"` QueryExecMode string `mapstructure:"queryExecMode"` azureEnv azure.EnvironmentSettings + awsEnv aws.EnvironmentSettings } // Reset the object. @@ -43,34 +47,61 @@ func (m *PostgresAuthMetadata) Reset() { m.ConnectionMaxIdleTime = 0 m.MaxConns = 0 m.UseAzureAD = false + m.UseAWSIAM = false m.QueryExecMode = "" } +type InitWithMetadataOpts struct { + AzureADEnabled bool + AWSIAMEnabled bool +} + // InitWithMetadata inits the object with metadata from the user. // Set azureADEnabled to true if the component can support authentication with Azure AD. // This is different from the "useAzureAD" property from the user, which is provided by the user and instructs the component to authenticate using Azure AD. -func (m *PostgresAuthMetadata) InitWithMetadata(meta map[string]string, azureADEnabled bool) (err error) { +func (m *PostgresAuthMetadata) InitWithMetadata(meta map[string]string, opts InitWithMetadataOpts) (err error) { // Validate input if m.ConnectionString == "" { return errors.New("missing connection string") } - - // Populate the Azure environment if using Azure AD - if azureADEnabled && m.UseAzureAD { + switch { + case opts.AzureADEnabled && m.UseAzureAD: m.azureEnv, err = azure.NewEnvironmentSettings(meta) if err != nil { return err } - } else { - // Make sure this is false + case opts.AWSIAMEnabled && m.UseAWSIAM: + m.awsEnv, err = aws.NewEnvironmentSettings(meta) + if err != nil { + return err + } + default: + // Make sure these are false m.UseAzureAD = false + m.UseAWSIAM = false } return nil } +func (m *PostgresAuthMetadata) ValidateAwsIamFields() (string, string, string, error) { + awsRegion, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "AWSRegion") + if awsRegion == "" { + return "", "", "", errors.New("metadata property AWSRegion is missing") + } + awsAccessKey, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "AWSAccessKey") + if awsAccessKey == "" { + return "", "", "", errors.New("metadata property AWSAccessKey is missing") + } + awsSecretKey, _ := metadata.GetMetadataProperty(m.awsEnv.Metadata, "AWSSecretKey") + if awsSecretKey == "" { + return "", "", "", errors.New("metadata property AWSSecretKey is missing") + } + return awsRegion, awsAccessKey, awsSecretKey, nil +} + // GetPgxPoolConfig returns the pgxpool.Config object that contains the credentials for connecting to PostgreSQL. -func (m *PostgresAuthMetadata) GetPgxPoolConfig() (*pgxpool.Config, error) { +func (m *PostgresAuthMetadata) GetPgxPoolConfig(ctx context.Context) (*pgxpool.Config, error) { // Get the config from the connection string config, err := pgxpool.ParseConfig(m.ConnectionString) if err != nil { @@ -112,19 +143,39 @@ func (m *PostgresAuthMetadata) GetPgxPoolConfig() (*pgxpool.Config, error) { // This is because tokens expire, and connections can drop and need to be re-established at any time // Fortunately, we can do this with the "BeforeConnect" hook config.BeforeConnect = func(ctx context.Context, cc *pgx.ConnConfig) error { - at, err := tokenCred.GetToken(ctx, policy.TokenRequestOptions{ + at, errGetAccessToken := tokenCred.GetToken(ctx, policy.TokenRequestOptions{ Scopes: []string{ m.azureEnv.Cloud.Services[azure.ServiceOSSRDBMS].Audience + "/.default", }, }) - if err != nil { - return err + if errGetAccessToken != nil { + return errGetAccessToken } cc.Password = at.Token return nil } } + if m.UseAWSIAM { + awsRegion, awsAccessKey, awsSecretKey, err := m.ValidateAwsIamFields() + if err != nil { + err = fmt.Errorf("failed to validate AWS IAM authentication fields: %v", err) + return nil, err + } + awsOpts := aws.AWSIAMAuthOptions{ + PoolConfig: config, + ConnectionString: m.ConnectionString, + Region: awsRegion, + AccessKey: awsAccessKey, + SecretKey: awsSecretKey, + } + + err = awsOpts.InitiateAWSIAMAuth(ctx) + if err != nil { + err = fmt.Errorf("failed to initiate AWS IAM authentication rotation dynamically: %v", err) + return nil, err + } + } return config, nil } diff --git a/common/component/kafka/kafka.go b/common/component/kafka/kafka.go index 8e0c9c91d4..8b2be83123 100644 --- a/common/component/kafka/kafka.go +++ b/common/component/kafka/kafka.go @@ -178,7 +178,7 @@ func (k *Kafka) Init(ctx context.Context, metadata map[string]string) error { case certificateAuthType: // already handled in updateTLSConfig case awsIAMAuthType: - k.logger.Info("Configuring AWS IAM authentcation") + k.logger.Info("Configuring AWS IAM authentication") err = updateAWSIAMAuthInfo(k.internalContext, config, meta) if err != nil { return err diff --git a/common/component/postgresql/v1/metadata.go b/common/component/postgresql/v1/metadata.go index 54f8df66c5..72a26a226b 100644 --- a/common/component/postgresql/v1/metadata.go +++ b/common/component/postgresql/v1/metadata.go @@ -17,6 +17,7 @@ import ( "errors" "time" + "github.com/dapr/components-contrib/common/authentication/aws" pgauth "github.com/dapr/components-contrib/common/authentication/postgresql" "github.com/dapr/components-contrib/state" "github.com/dapr/kit/metadata" @@ -37,9 +38,11 @@ type pgMetadata struct { MetadataTableName string `mapstructure:"metadataTableName"` // Could be in the format "schema.table" or just "table" Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"` CleanupInterval *time.Duration `mapstructure:"cleanupInterval" mapstructurealiases:"cleanupIntervalInSeconds"` + + aws.AWSIAM `mapstructure:",squash"` } -func (m *pgMetadata) InitWithMetadata(meta state.Metadata, azureADEnabled bool) error { +func (m *pgMetadata) InitWithMetadata(meta state.Metadata, opts pgauth.InitWithMetadataOpts) error { // Reset the object m.PostgresAuthMetadata.Reset() m.TableName = defaultTableName @@ -54,7 +57,7 @@ func (m *pgMetadata) InitWithMetadata(meta state.Metadata, azureADEnabled bool) } // Validate and sanitize input - err = m.PostgresAuthMetadata.InitWithMetadata(meta.Properties, azureADEnabled) + err = m.PostgresAuthMetadata.InitWithMetadata(meta.Properties, opts) if err != nil { return err } diff --git a/common/component/postgresql/v1/metadata_test.go b/common/component/postgresql/v1/metadata_test.go index 2a172dd870..e714bf2e09 100644 --- a/common/component/postgresql/v1/metadata_test.go +++ b/common/component/postgresql/v1/metadata_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/dapr/components-contrib/common/authentication/postgresql" "github.com/dapr/components-contrib/metadata" "github.com/dapr/components-contrib/state" ) @@ -29,7 +30,8 @@ func TestMetadata(t *testing.T) { m := pgMetadata{} props := map[string]string{} - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.Error(t, err) require.ErrorContains(t, err, "connection string") }) @@ -40,7 +42,8 @@ func TestMetadata(t *testing.T) { "connectionString": "foo", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) }) @@ -50,7 +53,8 @@ func TestMetadata(t *testing.T) { "connectionString": "foo", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) assert.Equal(t, defaultTableName, m.TableName) }) @@ -62,7 +66,8 @@ func TestMetadata(t *testing.T) { "tableName": "mytable", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) assert.Equal(t, "mytable", m.TableName) }) @@ -73,7 +78,8 @@ func TestMetadata(t *testing.T) { "connectionString": "foo", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) assert.Equal(t, defaultTimeout, m.Timeout) }) @@ -85,7 +91,8 @@ func TestMetadata(t *testing.T) { "timeout": "NaN", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.Error(t, err) }) @@ -96,7 +103,8 @@ func TestMetadata(t *testing.T) { "timeout": "42", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) assert.Equal(t, 42*time.Second, m.Timeout) }) @@ -108,7 +116,8 @@ func TestMetadata(t *testing.T) { "timeout": "0", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.Error(t, err) }) @@ -118,7 +127,8 @@ func TestMetadata(t *testing.T) { "connectionString": "foo", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) require.NotNil(t, m.CleanupInterval) assert.Equal(t, defaultCleanupInternal, *m.CleanupInterval) @@ -131,7 +141,8 @@ func TestMetadata(t *testing.T) { "cleanupInterval": "NaN", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.Error(t, err) }) @@ -142,7 +153,8 @@ func TestMetadata(t *testing.T) { "cleanupInterval": "42", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) require.NotNil(t, m.CleanupInterval) assert.Equal(t, 42*time.Second, *m.CleanupInterval) @@ -155,7 +167,8 @@ func TestMetadata(t *testing.T) { "cleanupIntervalInSeconds": "42", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) require.NotNil(t, m.CleanupInterval) assert.Equal(t, 42*time.Second, *m.CleanupInterval) @@ -168,7 +181,8 @@ func TestMetadata(t *testing.T) { "cleanupInterval": "42m", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) require.NotNil(t, m.CleanupInterval) assert.Equal(t, 42*time.Minute, *m.CleanupInterval) @@ -181,7 +195,8 @@ func TestMetadata(t *testing.T) { "cleanupIntervalInseconds": "42m", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) require.NotNil(t, m.CleanupInterval) assert.Equal(t, 42*time.Minute, *m.CleanupInterval) @@ -194,7 +209,8 @@ func TestMetadata(t *testing.T) { "cleanupInterval": "0", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) assert.Nil(t, m.CleanupInterval) }) @@ -206,7 +222,8 @@ func TestMetadata(t *testing.T) { "cleanupIntervalInSeconds": "0", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) assert.Nil(t, m.CleanupInterval) }) @@ -218,7 +235,8 @@ func TestMetadata(t *testing.T) { "cleanupInterval": "", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) require.NotNil(t, m.CleanupInterval) assert.Equal(t, defaultCleanupInternal, *m.CleanupInterval) @@ -231,7 +249,8 @@ func TestMetadata(t *testing.T) { "cleanupIntervalInSeconds": "", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) require.NotNil(t, m.CleanupInterval) assert.Equal(t, defaultCleanupInternal, *m.CleanupInterval) diff --git a/common/component/postgresql/v1/postgresql.go b/common/component/postgresql/v1/postgresql.go index 141242c604..ba3afc7072 100644 --- a/common/component/postgresql/v1/postgresql.go +++ b/common/component/postgresql/v1/postgresql.go @@ -28,6 +28,7 @@ import ( "github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgxpool" + pgauth "github.com/dapr/components-contrib/common/authentication/postgresql" pginterfaces "github.com/dapr/components-contrib/common/component/postgresql/interfaces" pgtransactions "github.com/dapr/components-contrib/common/component/postgresql/transactions" commonsql "github.com/dapr/components-contrib/common/component/sql" @@ -52,6 +53,7 @@ type PostgreSQL struct { setQueryFn func(*state.SetRequest, SetQueryOptions) string etagColumn string enableAzureAD bool + enableAWSIAM bool } type Options struct { @@ -59,6 +61,7 @@ type Options struct { SetQueryFn func(*state.SetRequest, SetQueryOptions) string ETagColumn string EnableAzureAD bool + EnableAWSIAM bool } type MigrateOptions struct { @@ -80,6 +83,7 @@ func NewPostgreSQLStateStore(logger logger.Logger, opts Options) state.Store { setQueryFn: opts.SetQueryFn, etagColumn: opts.ETagColumn, enableAzureAD: opts.EnableAzureAD, + enableAWSIAM: opts.EnableAWSIAM, } s.BulkStore = state.NewDefaultBulkStore(s) return s @@ -87,15 +91,18 @@ func NewPostgreSQLStateStore(logger logger.Logger, opts Options) state.Store { // Init sets up Postgres connection and performs migrations. func (p *PostgreSQL) Init(ctx context.Context, meta state.Metadata) error { - err := p.metadata.InitWithMetadata(meta, p.enableAzureAD) + opts := pgauth.InitWithMetadataOpts{ + AzureADEnabled: p.enableAzureAD, + AWSIAMEnabled: p.enableAWSIAM, + } + + err := p.metadata.InitWithMetadata(meta, opts) if err != nil { - p.logger.Errorf("Failed to parse metadata: %v", err) - return err + return fmt.Errorf("failed to parse metadata: %w", err) } - config, err := p.metadata.GetPgxPoolConfig() + config, err := p.metadata.GetPgxPoolConfig(ctx) if err != nil { - p.logger.Error(err) return err } @@ -103,18 +110,14 @@ func (p *PostgreSQL) Init(ctx context.Context, meta state.Metadata) error { p.db, err = pgxpool.NewWithConfig(connCtx, config) connCancel() if err != nil { - err = fmt.Errorf("failed to connect to the database: %w", err) - p.logger.Error(err) - return err + return fmt.Errorf("failed to connect to the database: %w", err) } pingCtx, pingCancel := context.WithTimeout(ctx, p.metadata.Timeout) err = p.db.Ping(pingCtx) pingCancel() if err != nil { - err = fmt.Errorf("failed to ping the database: %w", err) - p.logger.Error(err) - return err + return fmt.Errorf("failed to ping the database: %w", err) } err = p.migrateFn(ctx, p.db, MigrateOptions{ diff --git a/configuration/postgres/metadata.go b/configuration/postgres/metadata.go index 6b07053d38..e4d6492246 100644 --- a/configuration/postgres/metadata.go +++ b/configuration/postgres/metadata.go @@ -17,15 +17,21 @@ import ( "fmt" "time" + "github.com/dapr/components-contrib/common/authentication/aws" pgauth "github.com/dapr/components-contrib/common/authentication/postgresql" kitmd "github.com/dapr/kit/metadata" ) +const ( + defaultTimeout = 20 * time.Second // Default timeout for network requests +) + type metadata struct { pgauth.PostgresAuthMetadata `mapstructure:",squash"` - - ConfigTable string `mapstructure:"table"` - MaxIdleTimeoutOld time.Duration `mapstructure:"connMaxIdleTime"` // Deprecated alias for "connectionMaxIdleTime" + Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"` + ConfigTable string `mapstructure:"table"` + MaxIdleTimeoutOld time.Duration `mapstructure:"connMaxIdleTime"` // Deprecated alias for "connectionMaxIdleTime" + aws.AWSIAM `mapstructure:",squash"` } func (m *metadata) InitWithMetadata(meta map[string]string) error { @@ -33,6 +39,7 @@ func (m *metadata) InitWithMetadata(meta map[string]string) error { m.PostgresAuthMetadata.Reset() m.ConfigTable = "" m.MaxIdleTimeoutOld = 0 + m.Timeout = defaultTimeout err := kitmd.DecodeMetadata(meta, &m) if err != nil { @@ -55,8 +62,13 @@ func (m *metadata) InitWithMetadata(meta map[string]string) error { return fmt.Errorf("invalid table name '%s'. non-alphanumerics or upper cased table names are not supported", m.ConfigTable) } - // Azure AD auth is supported for this component - err = m.PostgresAuthMetadata.InitWithMetadata(meta, true) + opts := pgauth.InitWithMetadataOpts{ + AzureADEnabled: true, + AWSIAMEnabled: true, + } + + // Azure AD & AWS IAM auth is supported for this component + err = m.PostgresAuthMetadata.InitWithMetadata(meta, opts) if err != nil { return err } diff --git a/configuration/postgres/metadata.yaml b/configuration/postgres/metadata.yaml index ddf768d59e..8b4443f0b7 100644 --- a/configuration/postgres/metadata.yaml +++ b/configuration/postgres/metadata.yaml @@ -28,6 +28,40 @@ builtinAuthenticationProfiles: example: | "host=mydb.postgres.database.azure.com user=myapplication port=5432 database=dapr_test sslmode=require" type: string + - name: "aws" + metadata: + - name: useAWSIAM + required: true + type: bool + example: '"true"' + description: | + Must be set to `true` to enable the component to retrieve access tokens from AWS IAM. + This authentication method only works with AWS Relational Database Service for PostgreSQL databases. + - name: awsRegion + type: string + required: true + description: | + The AWS Region where the MSK Kafka broker is deployed to. + example: '"us-east-1"' + - name: awsAccessKey + type: string + required: true + description: | + AWS access key associated with an IAM account. + example: '"AKIAIOSFODNN7EXAMPLE"' + - name: awsSecretKey + type: string + required: true + sensitive: true + description: | + The secret key associated with the access key. + example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"' + - name: awsSessionToken + type: string + sensitive: true + description: | + AWS session token to use. A session token is only required if you are using temporary security credentials. + example: '"TOKEN"' authenticationProfiles: - title: "Connection string" description: "Authenticate using a Connection String." @@ -40,6 +74,12 @@ authenticationProfiles: "host=localhost user=postgres password=example port=5432 connect_timeout=10 database=dapr_test" type: string metadata: + - name: timeout + required: false + description: Timeout for all database operations. + example: "30s" + default: "20s" + type: duration - name: table required: true description: The table name for configuration information. diff --git a/configuration/postgres/postgres.go b/configuration/postgres/postgres.go index 8188591895..50b0f786fb 100644 --- a/configuration/postgres/postgres.go +++ b/configuration/postgres/postgres.go @@ -80,10 +80,11 @@ func (p *ConfigurationStore) Init(ctx context.Context, metadata configuration.Me } p.ActiveSubscriptions = make(map[string]*subscription) - p.client, err = p.connectDB(ctx) + p.client, err = p.connectDB(ctx, p.metadata.ConnectionString) if err != nil { return fmt.Errorf("error connecting to configuration store: '%w'", err) } + err = p.client.Ping(ctx) if err != nil { return fmt.Errorf("unable to connect to configuration store: '%w'", err) @@ -284,19 +285,22 @@ func (p *ConfigurationStore) handleSubscribedChange(ctx context.Context, handler } } -func (p *ConfigurationStore) connectDB(ctx context.Context) (*pgxpool.Pool, error) { - config, err := p.metadata.GetPgxPoolConfig() +func (p *ConfigurationStore) connectDB(ctx context.Context, connStr string) (*pgxpool.Pool, error) { + config, err := p.metadata.GetPgxPoolConfig(ctx) if err != nil { - return nil, fmt.Errorf("postgres configuration store connection error : %w", err) + return nil, fmt.Errorf("PostgreSQL configuration store connection error: %s", err) } + pool, err := pgxpool.NewWithConfig(ctx, config) if err != nil { - return nil, fmt.Errorf("postgres configuration store connection error : %w", err) + return nil, fmt.Errorf("PostgreSQL configuration store connection error: %w", err) } + err = pool.Ping(ctx) if err != nil { - return nil, fmt.Errorf("postgres configuration store ping error : %w", err) + return nil, fmt.Errorf("PostgreSQL configuration store ping error: %w", err) } + return pool, nil } diff --git a/go.mod b/go.mod index 9971cd6243..f45f64fbfa 100644 --- a/go.mod +++ b/go.mod @@ -55,7 +55,7 @@ require ( github.com/cloudwego/kitex-examples v0.1.1 github.com/cyphar/filepath-securejoin v0.2.4 github.com/dancannon/gorethink v4.0.0+incompatible - github.com/dapr/kit v0.13.1-0.20240306152601-e33fbab74548 + github.com/dapr/kit v0.13.1-0.20240402103809-0c7cfce53d9e github.com/didip/tollbooth/v7 v7.0.1 github.com/eclipse/paho.mqtt.golang v1.4.3 github.com/fasthttp-contrib/sessions v0.0.0-20160905201309-74f6ac73d5d5 @@ -134,6 +134,8 @@ require ( sigs.k8s.io/yaml v1.4.0 ) +require github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.3.10 + require ( cloud.google.com/go v0.110.8 // indirect cloud.google.com/go/compute v1.23.1 // indirect @@ -168,8 +170,8 @@ require ( github.com/ardielle/ardielle-go v1.5.2 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 // indirect - github.com/aws/aws-sdk-go-v2/config v1.18.28 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.27 // indirect + github.com/aws/aws-sdk-go-v2/config v1.18.28 + github.com/aws/aws-sdk-go-v2/credentials v1.13.27 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 // indirect diff --git a/go.sum b/go.sum index 625d76f2ce..8ab32b7f7d 100644 --- a/go.sum +++ b/go.sum @@ -258,6 +258,8 @@ github.com/aws/aws-sdk-go-v2/credentials v1.13.27/go.mod h1:syOqAek45ZXZp29HlnRS github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5 h1:kP3Me6Fy3vdi+9uHd7YLr6ewPxRL+PU6y15urfTaamU= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5/go.mod h1:Gj7tm95r+QsDoN2Fhuz/3npQvcZbkEf5mL70n3Xfluc= +github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.3.10 h1:z6fAXB4HSuYjrE/P8RU3NdCaN+EPaeq/+80aisCjuF8= +github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.3.10/go.mod h1:PoPjOi7j+/DtKIGC58HRfcdWKBPYYXwdKnRG+po+hzo= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 h1:hMUCiE3Zi5AHrRNGf5j985u0WyqI6r2NULhUfo0N/No= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35/go.mod h1:ipR5PvpSPqIqL5Mi82BxLnfMkHVbmco8kUwO2xrCi0M= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 h1:yOpYx+FTBdpk/g+sBU6Cb1H0U/TLEcYYp66mYqsPpcc= @@ -452,8 +454,8 @@ github.com/dancannon/gorethink v4.0.0+incompatible h1:KFV7Gha3AuqT+gr0B/eKvGhbjm github.com/dancannon/gorethink v4.0.0+incompatible/go.mod h1:BLvkat9KmZc1efyYwhz3WnybhRZtgF1K929FD8z1avU= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= -github.com/dapr/kit v0.13.1-0.20240306152601-e33fbab74548 h1:rh6cYZ/2nnufSAhnz+fwmsyNWljt0uyxmfIcG6t499I= -github.com/dapr/kit v0.13.1-0.20240306152601-e33fbab74548/go.mod h1:dons8V2bF6MPR2yFdxtTa86PfaE7EJtKAOkZ9hOavBQ= +github.com/dapr/kit v0.13.1-0.20240402103809-0c7cfce53d9e h1:mLvqfGuppb6uhsijmwTlF5sZVtGvig+Ua5ESKF17SxA= +github.com/dapr/kit v0.13.1-0.20240402103809-0c7cfce53d9e/go.mod h1:dons8V2bF6MPR2yFdxtTa86PfaE7EJtKAOkZ9hOavBQ= github.com/dave/jennifer v1.4.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/pubsub/kafka/metadata.yaml b/pubsub/kafka/metadata.yaml index 51d03f7f2b..8d3638e837 100644 --- a/pubsub/kafka/metadata.yaml +++ b/pubsub/kafka/metadata.yaml @@ -168,7 +168,7 @@ authenticationProfiles: type: string sensitive: true description: | - AWS session token to use. A session token is only required if you are using\ntemporary security credentials. + AWS session token to use. A session token is only required if you are using temporary security credentials. example: '"TOKEN"' - name: awsIamRoleArn type: string diff --git a/state/postgresql/v1/metadata.yaml b/state/postgresql/v1/metadata.yaml index fa3199e5c7..bfb307ed62 100644 --- a/state/postgresql/v1/metadata.yaml +++ b/state/postgresql/v1/metadata.yaml @@ -35,6 +35,49 @@ builtinAuthenticationProfiles: example: | "host=mydb.postgres.database.azure.com user=myapplication port=5432 database=dapr_test sslmode=require" type: string + - name: "aws" + metadata: + - name: useAWSIAM + required: true + type: bool + example: '"true"' + description: | + Must be set to `true` to enable the component to retrieve access tokens from AWS IAM. + This authentication method only works with AWS Relational Database Service for PostgreSQL databases. + - name: connectionString + required: true + sensitive: true + description: | + The connection string for the PostgreSQL database + This must contain the user, which corresponds to the name of the user created inside PostgreSQL that maps to the AWS IAM policy. This connection string should not contain any password. Note that the database name field is denoted by dbname with AWS. + example: | + "host=mydb.postgres.database.aws.com user=myapplication port=5432 dbname=dapr_test sslmode=require" + type: string + - name: awsRegion + type: string + required: true + description: | + The AWS Region where the AWS Relational Database Service is deployed to. + example: '"us-east-1"' + - name: awsAccessKey + type: string + required: true + description: | + AWS access key associated with an IAM account. + example: '"AKIAIOSFODNN7EXAMPLE"' + - name: awsSecretKey + type: string + required: false + sensitive: true + description: | + The secret key associated with the access key. + example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"' + - name: awsSessionToken + type: string + sensitive: true + description: | + AWS session token to use. A session token is only required if you are using temporary security credentials. + example: '"TOKEN"' authenticationProfiles: - title: "Connection string" description: "Authenticate using a Connection String" diff --git a/state/postgresql/v1/postgresql.go b/state/postgresql/v1/postgresql.go index d8d2be92da..45ef373f4e 100644 --- a/state/postgresql/v1/postgresql.go +++ b/state/postgresql/v1/postgresql.go @@ -24,6 +24,7 @@ func NewPostgreSQLStateStore(logger logger.Logger) state.Store { return postgresql.NewPostgreSQLQueryStateStore(logger, postgresql.Options{ ETagColumn: "xmin", EnableAzureAD: true, + EnableAWSIAM: true, MigrateFn: performMigrations, SetQueryFn: func(req *state.SetRequest, opts postgresql.SetQueryOptions) string { // Sprintf is required for table name because the driver does not substitute parameters for table names. diff --git a/state/postgresql/v2/metadata.go b/state/postgresql/v2/metadata.go index a21829807d..7903d74a29 100644 --- a/state/postgresql/v2/metadata.go +++ b/state/postgresql/v2/metadata.go @@ -17,6 +17,7 @@ import ( "errors" "time" + "github.com/dapr/components-contrib/common/authentication/aws" pgauth "github.com/dapr/components-contrib/common/authentication/postgresql" "github.com/dapr/components-contrib/state" "github.com/dapr/kit/metadata" @@ -41,9 +42,11 @@ type pgMetadata struct { MetadataTableName string `mapstructure:"metadataTableName"` // Could be in the format "schema.table" or just "table" Timeout time.Duration `mapstructure:"timeout" mapstructurealiases:"timeoutInSeconds"` CleanupInterval *time.Duration `mapstructure:"cleanupInterval" mapstructurealiases:"cleanupIntervalInSeconds"` + + aws.AWSIAM `mapstructure:",squash"` } -func (m *pgMetadata) InitWithMetadata(meta state.Metadata, azureADEnabled bool) error { +func (m *pgMetadata) InitWithMetadata(meta state.Metadata, opts pgauth.InitWithMetadataOpts) error { // Reset the object m.PostgresAuthMetadata.Reset() m.TablePrefix = "" @@ -58,7 +61,7 @@ func (m *pgMetadata) InitWithMetadata(meta state.Metadata, azureADEnabled bool) } // Validate and sanitize input - err = m.PostgresAuthMetadata.InitWithMetadata(meta.Properties, azureADEnabled) + err = m.PostgresAuthMetadata.InitWithMetadata(meta.Properties, opts) if err != nil { return err } diff --git a/state/postgresql/v2/metadata.yaml b/state/postgresql/v2/metadata.yaml index de53cdbcbc..8d3234fcb8 100644 --- a/state/postgresql/v2/metadata.yaml +++ b/state/postgresql/v2/metadata.yaml @@ -32,8 +32,51 @@ builtinAuthenticationProfiles: The connection string for the PostgreSQL database This must contain the user, which corresponds to the name of the user created inside PostgreSQL that maps to the Azure AD identity; this is often the name of the corresponding principal (e.g. the name of the Azure AD application). This connection string should not contain any password. example: | - "host=mydb.postgres.database.azure.com user=myapplication port=5432 database=dapr_test sslmode=require" + "host=mydb.postgres.database.azure.com user=myapplication port=5432 database=dapr_test password=masterpassword sslmode=require" type: string + - name: "aws" + metadata: + - name: useAWSIAM + required: true + type: bool + example: '"true"' + description: | + Must be set to `true` to enable the component to retrieve access tokens from AWS IAM. + This authentication method only works with AWS Relational Database Service for PostgreSQL databases. + - name: connectionString + required: true + sensitive: true + description: | + The connection string for the PostgreSQL database + This must contain the user, which corresponds to the name of the user created inside PostgreSQL that maps to the AWS IAM policy. This connection string should not contain any password. Note that the database name field is denoted by dbname with AWS. + example: | + "host=mydb.postgres.database.aws.com user=myapplication port=5432 dbname=dapr_test sslmode=require" + type: string + - name: awsRegion + type: string + required: true + description: | + The AWS Region where the AWS Relational Database Service is deployed to. + example: '"us-east-1"' + - name: awsAccessKey + type: string + required: false + description: | + AWS access key associated with an IAM account. + example: '"AKIAIOSFODNN7EXAMPLE"' + - name: awsSecretKey + type: string + required: false + sensitive: true + description: | + The secret key associated with the access key. + example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"' + - name: awsSessionToken + type: string + sensitive: true + description: | + AWS session token to use. A session token is only required if you are using temporary security credentials. + example: '"TOKEN"' authenticationProfiles: - title: "Connection string" description: "Authenticate using a Connection String" diff --git a/state/postgresql/v2/metadata_test.go b/state/postgresql/v2/metadata_test.go index 9596a0755c..a645f20e25 100644 --- a/state/postgresql/v2/metadata_test.go +++ b/state/postgresql/v2/metadata_test.go @@ -20,6 +20,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/dapr/components-contrib/common/authentication/postgresql" "github.com/dapr/components-contrib/metadata" "github.com/dapr/components-contrib/state" ) @@ -29,7 +30,8 @@ func TestMetadata(t *testing.T) { m := pgMetadata{} props := map[string]string{} - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.Error(t, err) require.ErrorContains(t, err, "connection string") }) @@ -40,7 +42,8 @@ func TestMetadata(t *testing.T) { "connectionString": "foo", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) }) @@ -50,7 +53,8 @@ func TestMetadata(t *testing.T) { "connectionString": "foo", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) assert.Equal(t, "state", m.TableName(pgTableState)) }) @@ -62,7 +66,8 @@ func TestMetadata(t *testing.T) { "tablePrefix": "my_", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) assert.Equal(t, "my_state", m.TableName(pgTableState)) }) @@ -73,7 +78,8 @@ func TestMetadata(t *testing.T) { "connectionString": "foo", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) assert.Equal(t, 20*time.Second, m.Timeout) }) @@ -85,7 +91,9 @@ func TestMetadata(t *testing.T) { "timeout": "NaN", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.Error(t, err) }) @@ -96,7 +104,8 @@ func TestMetadata(t *testing.T) { "timeout": "42", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) assert.Equal(t, 42*time.Second, m.Timeout) }) @@ -108,7 +117,8 @@ func TestMetadata(t *testing.T) { "timeout": "0", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.Error(t, err) }) @@ -118,7 +128,9 @@ func TestMetadata(t *testing.T) { "connectionString": "foo", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) _ = assert.NotNil(t, m.CleanupInterval) && assert.Equal(t, time.Hour, *m.CleanupInterval) @@ -131,7 +143,8 @@ func TestMetadata(t *testing.T) { "cleanupInterval": "NaN", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.Error(t, err) }) @@ -142,7 +155,8 @@ func TestMetadata(t *testing.T) { "cleanupInterval": "42", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) _ = assert.NotNil(t, m.CleanupInterval) && assert.Equal(t, 42*time.Second, *m.CleanupInterval) @@ -155,7 +169,8 @@ func TestMetadata(t *testing.T) { "cleanupIntervalInSeconds": "42", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) _ = assert.NotNil(t, m.CleanupInterval) && assert.Equal(t, 42*time.Second, *m.CleanupInterval) @@ -168,7 +183,8 @@ func TestMetadata(t *testing.T) { "cleanupInterval": "42m", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) _ = assert.NotNil(t, m.CleanupInterval) && assert.Equal(t, 42*time.Minute, *m.CleanupInterval) @@ -181,7 +197,8 @@ func TestMetadata(t *testing.T) { "cleanupIntervalInseconds": "42m", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) _ = assert.NotNil(t, m.CleanupInterval) && assert.Equal(t, 42*time.Minute, *m.CleanupInterval) @@ -194,7 +211,8 @@ func TestMetadata(t *testing.T) { "cleanupInterval": "0", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) assert.Nil(t, m.CleanupInterval) }) @@ -206,7 +224,8 @@ func TestMetadata(t *testing.T) { "cleanupIntervalInSeconds": "0", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) assert.Nil(t, m.CleanupInterval) }) @@ -218,7 +237,8 @@ func TestMetadata(t *testing.T) { "cleanupInterval": "", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) _ = assert.NotNil(t, m.CleanupInterval) && assert.Equal(t, defaultCleanupInternal, *m.CleanupInterval) @@ -231,7 +251,8 @@ func TestMetadata(t *testing.T) { "cleanupIntervalInSeconds": "", } - err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, false) + opts := postgresql.InitWithMetadataOpts{} + err := m.InitWithMetadata(state.Metadata{Base: metadata.Base{Properties: props}}, opts) require.NoError(t, err) _ = assert.NotNil(t, m.CleanupInterval) && assert.Equal(t, defaultCleanupInternal, *m.CleanupInterval) diff --git a/state/postgresql/v2/postgresql.go b/state/postgresql/v2/postgresql.go index 06449c0278..cd44e8cba3 100644 --- a/state/postgresql/v2/postgresql.go +++ b/state/postgresql/v2/postgresql.go @@ -27,6 +27,7 @@ import ( "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgxpool" + pgauth "github.com/dapr/components-contrib/common/authentication/postgresql" pginterfaces "github.com/dapr/components-contrib/common/component/postgresql/interfaces" pgtransactions "github.com/dapr/components-contrib/common/component/postgresql/transactions" sqlinternal "github.com/dapr/components-contrib/common/component/sql" @@ -35,6 +36,7 @@ import ( "github.com/dapr/components-contrib/state" stateutils "github.com/dapr/components-contrib/state/utils" "github.com/dapr/kit/logger" + "github.com/dapr/kit/utils" ) // PostgreSQL state store. @@ -48,12 +50,18 @@ type PostgreSQL struct { gc sqlinternal.GarbageCollector enableAzureAD bool + + enableAWSIAM bool } type Options struct { // Disables support for authenticating with Azure AD // This should be set to "false" when targeting different databases than PostgreSQL (such as CockroachDB) NoAzureAD bool + + // Disables support for authenticating with AWS IAM + // This should be set to "false" when targeting different databases than PostgreSQL (such as CockroachDB) + NoAWSIAM bool } // NewPostgreSQLStateStore creates a new instance of PostgreSQL state store v2 with the default options. @@ -63,11 +71,19 @@ func NewPostgreSQLStateStore(logger logger.Logger) state.Store { return NewPostgreSQLStateStoreWithOptions(logger, Options{}) } +// NewPostgreSQLStateStoreWithTrueOptions creates a new instance of PostgreSQL state store v2 with Azure AD authentication and AWS IAM authentication disabled. +// The v2 of the component uses a different format for storing data, always in a BYTEA column, which is more efficient than the JSONB column used in v1. +// Additionally, v2 uses random UUIDs for etags instead of the xmin column, expanding support to all Postgres-compatible databases such as CockroachDB, etc. +func NewPostgreSQLStateStoreWithTrueOptions(logger logger.Logger) state.Store { + return NewPostgreSQLStateStoreWithOptions(logger, Options{NoAWSIAM: true, NoAzureAD: true}) +} + // NewPostgreSQLStateStoreWithOptions creates a new instance of PostgreSQL state store with options. func NewPostgreSQLStateStoreWithOptions(logger logger.Logger, opts Options) state.Store { s := &PostgreSQL{ logger: logger, enableAzureAD: !opts.NoAzureAD, + enableAWSIAM: !opts.NoAWSIAM, } s.BulkStore = state.NewDefaultBulkStore(s) return s @@ -75,24 +91,37 @@ func NewPostgreSQLStateStoreWithOptions(logger logger.Logger, opts Options) stat // Init sets up Postgres connection and performs migrations func (p *PostgreSQL) Init(ctx context.Context, meta state.Metadata) error { - err := p.metadata.InitWithMetadata(meta, p.enableAzureAD) + var ( + useAWS bool + useAzure bool + err error + ) + + awsIam, _ := metadata.GetMetadataProperty(meta.Properties, "UseAWSIAM") + useAWS = utils.IsTruthy(awsIam) + azureAd, _ := metadata.GetMetadataProperty(meta.Properties, "UseAzureAD") + useAzure = utils.IsTruthy(azureAd) + + opts := pgauth.InitWithMetadataOpts{ + AzureADEnabled: useAzure, + AWSIAMEnabled: useAWS, + } + + err = p.metadata.InitWithMetadata(meta, opts) if err != nil { - p.logger.Errorf("Failed to parse metadata: %v", err) return err } - config, err := p.metadata.GetPgxPoolConfig() + config, err := p.metadata.GetPgxPoolConfig(ctx) if err != nil { - p.logger.Error(err) return err } connCtx, connCancel := context.WithTimeout(ctx, p.metadata.Timeout) p.db, err = pgxpool.NewWithConfig(connCtx, config) - connCancel() + defer connCancel() if err != nil { err = fmt.Errorf("failed to connect to the database: %w", err) - p.logger.Error(err) return err } @@ -101,14 +130,12 @@ func (p *PostgreSQL) Init(ctx context.Context, meta state.Metadata) error { pingCancel() if err != nil { err = fmt.Errorf("failed to ping the database: %w", err) - p.logger.Error(err) return err } // Migrate schema err = p.performMigrations(ctx) if err != nil { - p.logger.Error(err) return err } diff --git a/tests/certification/embedded/embedded.go b/tests/certification/embedded/embedded.go index 11c7e5ff6e..33031045a8 100644 --- a/tests/certification/embedded/embedded.go +++ b/tests/certification/embedded/embedded.go @@ -50,7 +50,7 @@ const ( maxConcurrency = -1 enableMTLS = false sentryAddress = "" - maxRequestBodySize = 4 + maxRequestBodySize = 4 << 20 daprHTTPPort = runtime.DefaultDaprHTTPPort daprAPIGRPCPort = runtime.DefaultDaprAPIGRPCPort @@ -193,8 +193,8 @@ func NewRuntime(ctx context.Context, appID string, opts ...Option) (*runtime.Dap AppMaxConcurrency: maxConcurrency, EnableMTLS: enableMTLS, SentryAddress: sentryAddress, - DaprHTTPMaxRequestSize: maxRequestBodySize, - DaprHTTPReadBufferSize: runtime.DefaultReadBufferSize, + MaxRequestSize: maxRequestBodySize, + ReadBufferSize: runtime.DefaultReadBufferSize, DaprGracefulShutdownSeconds: 1, EnableAPILogging: ptr.Of(true), DisableBuiltinK8sSecretStore: false, diff --git a/tests/certification/go.mod b/tests/certification/go.mod index b476bdd656..3b9ad855f1 100644 --- a/tests/certification/go.mod +++ b/tests/certification/go.mod @@ -19,10 +19,10 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 github.com/cloudwego/kitex v0.5.0 github.com/cloudwego/kitex-examples v0.1.1 - github.com/dapr/components-contrib v1.13.0-rc.10 - github.com/dapr/dapr v1.13.0 - github.com/dapr/go-sdk v1.10.1 - github.com/dapr/kit v0.13.1-0.20240306152601-e33fbab74548 + github.com/dapr/components-contrib v1.13.1 + github.com/dapr/dapr v1.13.0-rc.1.0.20240430161153-091a20466b13 + github.com/dapr/go-sdk v1.8.0 + github.com/dapr/kit v0.13.1-0.20240402103809-0c7cfce53d9e github.com/eclipse/paho.mqtt.golang v1.4.3 github.com/go-chi/chi/v5 v5.0.12 github.com/go-redis/redis/v8 v8.11.5 @@ -39,6 +39,7 @@ require ( go.mongodb.org/mongo-driver v1.12.1 go.uber.org/multierr v1.11.0 go.uber.org/ratelimit v0.3.0 + google.golang.org/protobuf v1.33.0 k8s.io/utils v0.0.0-20240102154912-e7106e64919e modernc.org/sqlite v1.28.0 ) @@ -67,6 +68,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue v1.0.0 // indirect github.com/Azure/go-amqp v1.0.5 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect + github.com/BurntSushi/toml v1.1.0 // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/PuerkitoBio/purell v1.2.1 // indirect @@ -87,6 +89,7 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.18.28 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.13.27 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5 // indirect + github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.3.10 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.36 // indirect @@ -168,6 +171,7 @@ require ( github.com/google/s2a-go v0.1.7 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/grandcat/zeroconf v1.0.0 // indirect @@ -219,7 +223,7 @@ require ( github.com/lestrrat-go/option v1.0.1 // indirect github.com/linkedin/goavro/v2 v2.12.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/magiconair/properties v1.8.6 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/marusama/semaphore/v2 v2.5.0 // indirect github.com/matoous/go-nanoid/v2 v2.0.0 // indirect @@ -265,12 +269,14 @@ require ( github.com/segmentio/asm v1.2.0 // indirect github.com/shirou/gopsutil/v3 v3.22.2 // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/smartystreets/assertions v1.1.0 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spiffe/go-spiffe/v2 v2.1.6 // indirect + github.com/spf13/viper v1.15.0 // indirect + github.com/spiffe/go-spiffe/v2 v2.1.7 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect @@ -324,7 +330,6 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect google.golang.org/grpc v1.62.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/tests/certification/go.sum b/tests/certification/go.sum index d2802e20ef..fa3c091097 100644 --- a/tests/certification/go.sum +++ b/tests/certification/go.sum @@ -97,8 +97,9 @@ github.com/Azure/go-amqp v1.0.5 h1:po5+ljlcNSU8xtapHTe8gIc8yHxCzC03E8afH2g1ftU= github.com/Azure/go-amqp v1.0.5/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= @@ -110,8 +111,8 @@ github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXY github.com/IBM/sarama v1.42.2 h1:VoY4hVIZ+WQJ8G9KNY/SQlWguBQXQ9uvFPOnrcu8hEw= github.com/IBM/sarama v1.42.2/go.mod h1:FLPGUGwYqEs62hq2bVG6Io2+5n+pS6s/WOXVKWSLFtE= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= -github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= @@ -209,6 +210,8 @@ github.com/aws/aws-sdk-go-v2/credentials v1.13.27/go.mod h1:syOqAek45ZXZp29HlnRS github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5 h1:kP3Me6Fy3vdi+9uHd7YLr6ewPxRL+PU6y15urfTaamU= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.5/go.mod h1:Gj7tm95r+QsDoN2Fhuz/3npQvcZbkEf5mL70n3Xfluc= +github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.3.10 h1:z6fAXB4HSuYjrE/P8RU3NdCaN+EPaeq/+80aisCjuF8= +github.com/aws/aws-sdk-go-v2/feature/rds/auth v1.3.10/go.mod h1:PoPjOi7j+/DtKIGC58HRfcdWKBPYYXwdKnRG+po+hzo= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35 h1:hMUCiE3Zi5AHrRNGf5j985u0WyqI6r2NULhUfo0N/No= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.35/go.mod h1:ipR5PvpSPqIqL5Mi82BxLnfMkHVbmco8kUwO2xrCi0M= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.29 h1:yOpYx+FTBdpk/g+sBU6Cb1H0U/TLEcYYp66mYqsPpcc= @@ -390,12 +393,12 @@ github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53E github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0= -github.com/dapr/dapr v1.13.0 h1:yExu47iCyqBSghAGVjgVjica4NfFd0dVlPXQTpQWR98= -github.com/dapr/dapr v1.13.0/go.mod h1:VFjFGrLb84k5pjmWNn9reI5D28OQifdUbBdymXxbZDc= -github.com/dapr/go-sdk v1.10.1 h1:g6mM2RXyGkrzsqWFfCy8rw+UAt1edQEgRaQXT+XP4PE= -github.com/dapr/go-sdk v1.10.1/go.mod h1:lPjyF/xubh35fbdNdKkxBbFxFNCmta4zmvsk0JxuUG0= -github.com/dapr/kit v0.13.1-0.20240306152601-e33fbab74548 h1:rh6cYZ/2nnufSAhnz+fwmsyNWljt0uyxmfIcG6t499I= -github.com/dapr/kit v0.13.1-0.20240306152601-e33fbab74548/go.mod h1:dons8V2bF6MPR2yFdxtTa86PfaE7EJtKAOkZ9hOavBQ= +github.com/dapr/dapr v1.13.0-rc.1.0.20240430161153-091a20466b13 h1:6YsSeL6RtdCLGDdItIM1G4NaCYxZO7KkAAV+hLz9hMM= +github.com/dapr/dapr v1.13.0-rc.1.0.20240430161153-091a20466b13/go.mod h1:ag7ogrSRju3E/BeacqrcEKtna1a5FOss4p2cRtgp4Nw= +github.com/dapr/go-sdk v1.8.0 h1:OEleeL3zUTqXxIZ7Vkk3PClAeCh1g8sZ1yR2JFZKfXM= +github.com/dapr/go-sdk v1.8.0/go.mod h1:MBcTKXg8PmBc8A968tVWQg1Xt+DZtmeVR6zVVVGcmeA= +github.com/dapr/kit v0.13.1-0.20240402103809-0c7cfce53d9e h1:mLvqfGuppb6uhsijmwTlF5sZVtGvig+Ua5ESKF17SxA= +github.com/dapr/kit v0.13.1-0.20240402103809-0c7cfce53d9e/go.mod h1:dons8V2bF6MPR2yFdxtTa86PfaE7EJtKAOkZ9hOavBQ= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -516,8 +519,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= -github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= +github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= @@ -717,8 +720,9 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -981,8 +985,9 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marusama/semaphore/v2 v2.5.0 h1:o/1QJD9DBYOWRnDhPwDVAXQn6mQYD0gZaS1Tpx6DJGM= @@ -1127,6 +1132,8 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= @@ -1262,8 +1269,9 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= +github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -1278,24 +1286,27 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= +github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spiffe/go-spiffe/v2 v2.1.6 h1:4SdizuQieFyL9eNU+SPiCArH4kynzaKOOj0VvM8R7Xo= -github.com/spiffe/go-spiffe/v2 v2.1.6/go.mod h1:eVDqm9xFvyqao6C+eQensb9ZPkyNEeaUbqbBpOhBnNk= +github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= +github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/spiffe/go-spiffe/v2 v2.1.7 h1:VUkM1yIyg/x8X7u1uXqSRVRCdMdfRIEdFBzpqoeASGk= +github.com/spiffe/go-spiffe/v2 v2.1.7/go.mod h1:QJDGdhXllxjxvd5B+2XnhhXB/+rC8gr+lNrtOryiWeE= github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -1323,8 +1334,9 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= diff --git a/tests/certification/secretstores/hashicorp/vault/metadata_helpers.go b/tests/certification/secretstores/hashicorp/vault/metadata_helpers.go index 5334fd91db..29cc2bdef7 100644 --- a/tests/certification/secretstores/hashicorp/vault/metadata_helpers.go +++ b/tests/certification/secretstores/hashicorp/vault/metadata_helpers.go @@ -19,9 +19,9 @@ import ( "github.com/dapr/components-contrib/secretstores" "github.com/dapr/components-contrib/tests/certification/flow" - "github.com/dapr/dapr/pkg/proto/runtime/v1" "github.com/dapr/go-sdk/client" "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/types/known/emptypb" ) // @@ -74,7 +74,7 @@ func getComponentCapabilities(ctx flow.Context, currentGrpcPort int, targetCompo clientCtx := context.Background() - resp, err := daprClient.GrpcClient().GetMetadata(clientCtx, &runtime.GetMetadataRequest{}) + resp, err := daprClient.GrpcClient().GetMetadata(clientCtx, new(emptypb.Empty)) assert.NoError(ctx.T, err) assert.NotNil(ctx.T, resp) assert.NotNil(ctx.T, resp.GetRegisteredComponents()) diff --git a/tests/conformance/state_test.go b/tests/conformance/state_test.go index c799508960..1a8441742c 100644 --- a/tests/conformance/state_test.go +++ b/tests/conformance/state_test.go @@ -121,7 +121,7 @@ func loadStateStore(name string) state.Store { case "cockroachdb.v2": // v2 of the component is an alias for the PostgreSQL state store // We still have a conformance test to validate that the component works with CockroachDB - return s_postgresql_v2.NewPostgreSQLStateStoreWithOptions(testLogger, s_postgresql_v2.Options{NoAzureAD: true}) + return s_postgresql_v2.NewPostgreSQLStateStoreWithOptions(testLogger, s_postgresql_v2.Options{NoAzureAD: true, NoAWSIAM: true}) case "memcached": return s_memcached.NewMemCacheStateStore(testLogger) case "rethinkdb": diff --git a/tests/e2e/pubsub/jetstream/go.mod b/tests/e2e/pubsub/jetstream/go.mod index 28307780cd..eabd66d5b9 100644 --- a/tests/e2e/pubsub/jetstream/go.mod +++ b/tests/e2e/pubsub/jetstream/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.2 require ( github.com/dapr/components-contrib v1.10.6-0.20230403162214-9ee9d56cb7ea - github.com/dapr/kit v0.13.1-0.20240306152601-e33fbab74548 + github.com/dapr/kit v0.13.1-0.20240402103809-0c7cfce53d9e ) require ( diff --git a/tests/e2e/pubsub/jetstream/go.sum b/tests/e2e/pubsub/jetstream/go.sum index ac53fd570d..8223c7873c 100644 --- a/tests/e2e/pubsub/jetstream/go.sum +++ b/tests/e2e/pubsub/jetstream/go.sum @@ -4,8 +4,8 @@ github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.14.0 h1:dEopBSOSjB5f github.com/cloudevents/sdk-go/binding/format/protobuf/v2 v2.14.0/go.mod h1:qDSbb0fgIfFNjZrNTPtS5MOMScAGyQtn1KlSvoOdqYw= github.com/cloudevents/sdk-go/v2 v2.14.0 h1:Nrob4FwVgi5L4tV9lhjzZcjYqFVyJzsA56CwPaPfv6s= github.com/cloudevents/sdk-go/v2 v2.14.0/go.mod h1:xDmKfzNjM8gBvjaF8ijFjM1VYOVUEeUfapHMUX1T5To= -github.com/dapr/kit v0.13.1-0.20240306152601-e33fbab74548 h1:rh6cYZ/2nnufSAhnz+fwmsyNWljt0uyxmfIcG6t499I= -github.com/dapr/kit v0.13.1-0.20240306152601-e33fbab74548/go.mod h1:dons8V2bF6MPR2yFdxtTa86PfaE7EJtKAOkZ9hOavBQ= +github.com/dapr/kit v0.13.1-0.20240402103809-0c7cfce53d9e h1:mLvqfGuppb6uhsijmwTlF5sZVtGvig+Ua5ESKF17SxA= +github.com/dapr/kit v0.13.1-0.20240402103809-0c7cfce53d9e/go.mod h1:dons8V2bF6MPR2yFdxtTa86PfaE7EJtKAOkZ9hOavBQ= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= diff --git a/tests/utils/configupdater/postgres/postgres.go b/tests/utils/configupdater/postgres/postgres.go index 32a162d5cf..4aa80590ba 100644 --- a/tests/utils/configupdater/postgres/postgres.go +++ b/tests/utils/configupdater/postgres/postgres.go @@ -11,6 +11,7 @@ import ( pgauth "github.com/dapr/components-contrib/common/authentication/postgresql" "github.com/dapr/components-contrib/configuration" + "github.com/dapr/components-contrib/metadata" "github.com/dapr/components-contrib/tests/utils/configupdater" "github.com/dapr/kit/logger" "github.com/dapr/kit/utils" @@ -84,11 +85,16 @@ func (r *ConfigUpdater) CreateTrigger(channel string) error { } func (r *ConfigUpdater) Init(props map[string]string) error { + connString, _ := metadata.GetMetadataProperty(props, "connectionString") + useAzureAd, _ := metadata.GetMetadataProperty(props, "useAzureAD") + useAwsIam, _ := metadata.GetMetadataProperty(props, "useAWSIAM") + md := pgauth.PostgresAuthMetadata{ - ConnectionString: props["connectionString"], - UseAzureAD: utils.IsTruthy(props["useAzureAD"]), + ConnectionString: connString, + UseAzureAD: utils.IsTruthy(useAzureAd), + UseAWSIAM: utils.IsTruthy(useAwsIam), } - err := md.InitWithMetadata(props, true) + err := md.InitWithMetadata(props, pgauth.InitWithMetadataOpts{AzureADEnabled: true, AWSIAMEnabled: true}) if err != nil { return err } @@ -102,7 +108,7 @@ func (r *ConfigUpdater) Init(props map[string]string) error { return fmt.Errorf("missing postgreSQL configuration table name") } - config, err := md.GetPgxPoolConfig() + config, err := md.GetPgxPoolConfig(ctx) if err != nil { return fmt.Errorf("postgres configuration store connection error : %w", err) }