diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a2250f3e6d..67ab2d6da1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ To learn more about active deprecations, we recommend checking [GitHub Discussio ### Improvements - **General**: Metrics Adapter: remove deprecated Prometheus Metrics and non-gRPC code ([#3930](https://github.com/kedacore/keda/issues/3930)) +- **AWS DynamoDB:** Add support for `indexName` ([#4680](https://github.com/kedacore/keda/issues/4680)) - **Azure Data Explorer Scaler**: Use azidentity SDK ([#4489](https://github.com/kedacore/keda/issues/4489)) - **External Scaler**: Add tls options in TriggerAuth metadata. ([#3565](https://github.com/kedacore/keda/issues/3565)) - **GCP PubSub Scaler**: Make it more flexible for metrics ([#4243](https://github.com/kedacore/keda/issues/4243)) diff --git a/pkg/scalers/aws_dynamodb_scaler.go b/pkg/scalers/aws_dynamodb_scaler.go index 4d0d1cef9b3..01814f02594 100644 --- a/pkg/scalers/aws_dynamodb_scaler.go +++ b/pkg/scalers/aws_dynamodb_scaler.go @@ -32,6 +32,7 @@ type awsDynamoDBMetadata struct { keyConditionExpression string expressionAttributeNames map[string]*string expressionAttributeValues map[string]*dynamodb.AttributeValue + indexName string targetValue int64 activationTargetValue int64 awsAuthorization awsAuthorizationMetadata @@ -106,6 +107,10 @@ func parseAwsDynamoDBMetadata(config *ScalerConfig) (*awsDynamoDBMetadata, error meta.awsEndpoint = val } + if val, ok := config.TriggerMetadata["indexName"]; ok { + meta.indexName = val + } + if val, ok := config.TriggerMetadata["keyConditionExpression"]; ok && val != "" { meta.keyConditionExpression = val } else { @@ -218,6 +223,10 @@ func (s *awsDynamoDBScaler) GetQueryMetrics() (float64, error) { ExpressionAttributeValues: s.metadata.expressionAttributeValues, } + if s.metadata.indexName != "" { + dimensions.IndexName = aws.String(s.metadata.indexName) + } + res, err := s.dbClient.Query(&dimensions) if err != nil { s.logger.Error(err, "Failed to get output") diff --git a/pkg/scalers/aws_dynamodb_scaler_test.go b/pkg/scalers/aws_dynamodb_scaler_test.go index d2335f16cd8..9ce674f9d64 100644 --- a/pkg/scalers/aws_dynamodb_scaler_test.go +++ b/pkg/scalers/aws_dynamodb_scaler_test.go @@ -18,6 +18,7 @@ const ( testAWSDynamoSecretAccessKey = "none" testAWSDynamoErrorTable = "Error" testAWSDynamoNoValueTable = "NoValue" + testAWSDynamoIndexTable = "Index" ) var testAWSDynamoAuthentication = map[string]string{ @@ -226,6 +227,66 @@ var dynamoTestCases = []parseDynamoDBMetadataTestData{ }, }, }, + { + name: "properly formed dynamo name and region with activationTargetValue", + metadata: map[string]string{ + "tableName": "test", + "awsRegion": "eu-west-1", + "keyConditionExpression": "#yr = :yyyy", + "expressionAttributeNames": "{ \"#yr\" : \"year\" }", + "expressionAttributeValues": "{\":yyyy\": {\"N\": \"1994\"}}", + "activationTargetValue": "1", + "targetValue": "3", + }, + authParams: testAWSDynamoAuthentication, + expectedError: nil, + expectedMetadata: &awsDynamoDBMetadata{ + tableName: "test", + awsRegion: "eu-west-1", + keyConditionExpression: "#yr = :yyyy", + expressionAttributeNames: map[string]*string{"#yr": &year}, + expressionAttributeValues: map[string]*dynamodb.AttributeValue{":yyyy": &yearAttr}, + activationTargetValue: 1, + targetValue: 3, + scalerIndex: 1, + metricName: "s1-aws-dynamodb-test", + awsAuthorization: awsAuthorizationMetadata{ + awsAccessKeyID: "none", + awsSecretAccessKey: "none", + podIdentityOwner: true, + }, + }, + }, + { + name: "properly formed dynamo name and region with index name", + metadata: map[string]string{ + "tableName": "test", + "awsRegion": "eu-west-1", + "indexName": "test-index", + "keyConditionExpression": "#yr = :yyyy", + "expressionAttributeNames": "{ \"#yr\" : \"year\" }", + "expressionAttributeValues": "{\":yyyy\": {\"N\": \"1994\"}}", + "targetValue": "3", + }, + authParams: testAWSDynamoAuthentication, + expectedError: nil, + expectedMetadata: &awsDynamoDBMetadata{ + tableName: "test", + awsRegion: "eu-west-1", + indexName: "test-index", + keyConditionExpression: "#yr = :yyyy", + expressionAttributeNames: map[string]*string{"#yr": &year}, + expressionAttributeValues: map[string]*dynamodb.AttributeValue{":yyyy": &yearAttr}, + targetValue: 3, + scalerIndex: 1, + metricName: "s1-aws-dynamodb-test", + awsAuthorization: awsAuthorizationMetadata{ + awsAccessKeyID: "none", + awsSecretAccessKey: "none", + podIdentityOwner: true, + }, + }, + }, } func TestParseDynamoMetadata(t *testing.T) { @@ -253,6 +314,7 @@ type mockDynamoDB struct { } var result int64 = 4 +var indexResult int64 = 2 var empty int64 func (c *mockDynamoDB) Query(input *dynamodb.QueryInput) (*dynamodb.QueryOutput, error) { @@ -265,6 +327,12 @@ func (c *mockDynamoDB) Query(input *dynamodb.QueryInput) (*dynamodb.QueryOutput, }, nil } + if input.IndexName != nil { + return &dynamodb.QueryOutput{ + Count: &indexResult, + }, nil + } + return &dynamodb.QueryOutput{ Count: &result, }, nil @@ -299,6 +367,16 @@ var awsDynamoDBGetMetricTestData = []awsDynamoDBMetadata{ expressionAttributeValues: map[string]*dynamodb.AttributeValue{":yyyy": &yearAttr}, targetValue: 3, }, + { + tableName: testAWSDynamoIndexTable, + awsRegion: "eu-west-1", + indexName: "test-index", + keyConditionExpression: "#yr = :yyyy", + expressionAttributeNames: map[string]*string{"#yr": &year}, + expressionAttributeValues: map[string]*dynamodb.AttributeValue{":yyyy": &yearAttr}, + activationTargetValue: 3, + targetValue: 3, + }, } func TestDynamoGetMetrics(t *testing.T) { @@ -309,9 +387,11 @@ func TestDynamoGetMetrics(t *testing.T) { value, _, err := scaler.GetMetricsAndActivity(context.Background(), "aws-dynamodb") switch meta.tableName { case testAWSDynamoErrorTable: - assert.Error(t, err, "expect error because of dynamodb api error") + assert.EqualError(t, err, "error", "expect error because of dynamodb api error") case testAWSDynamoNoValueTable: assert.NoError(t, err, "dont expect error when returning empty result from dynamodb") + case testAWSDynamoIndexTable: + assert.EqualValues(t, int64(2), value[0].Value.Value()) default: assert.EqualValues(t, int64(4), value[0].Value.Value()) } @@ -327,9 +407,11 @@ func TestDynamoGetQueryMetrics(t *testing.T) { value, err := scaler.GetQueryMetrics() switch meta.tableName { case testAWSDynamoErrorTable: - assert.Error(t, err, "expect error because of dynamodb api error") + assert.EqualError(t, err, "error", "expect error because of dynamodb api error") case testAWSDynamoNoValueTable: - assert.NoError(t, err, "dont expect error when returning empty metric list from cloudwatch") + assert.NoError(t, err, "dont expect error when returning empty result from dynamodb") + case testAWSDynamoIndexTable: + assert.EqualValues(t, int64(2), value) default: assert.EqualValues(t, int64(4), value) } @@ -345,9 +427,11 @@ func TestDynamoIsActive(t *testing.T) { _, value, err := scaler.GetMetricsAndActivity(context.Background(), "aws-dynamodb") switch meta.tableName { case testAWSDynamoErrorTable: - assert.Error(t, err, "expect error because of cloudwatch api error") + assert.EqualError(t, err, "error", "expect error because of dynamodb api error") case testAWSDynamoNoValueTable: assert.NoError(t, err, "dont expect error when returning empty result from dynamodb") + case testAWSDynamoIndexTable: + assert.EqualValues(t, false, value) default: assert.EqualValues(t, true, value) }