diff --git a/integration/combination/test_function_with_self_managed_kafka.py b/integration/combination/test_function_with_self_managed_kafka.py new file mode 100644 index 000000000..b07b6f285 --- /dev/null +++ b/integration/combination/test_function_with_self_managed_kafka.py @@ -0,0 +1,21 @@ +from integration.helpers.base_test import BaseTest +from parameterized import parameterized + + +class TestFunctionWithSelfManagedKafka(BaseTest): + @parameterized.expand( + [ + "combination/function_with_self_managed_kafka", + "combination/function_with_self_managed_kafka_intrinsics", + ] + ) + def test_function_with_self_managed_kafka(self, file_name): + self.create_and_verify_stack(file_name) + # Get the notification configuration and make sure Lambda Function connection is added + lambda_client = self.client_provider.lambda_client + function_name = self.get_physical_id_by_type("AWS::Lambda::Function") + lambda_function_arn = lambda_client.get_function_configuration(FunctionName=function_name)["FunctionArn"] + event_source_mapping_id = self.get_physical_id_by_type("AWS::Lambda::EventSourceMapping") + event_source_mapping_result = lambda_client.get_event_source_mapping(UUID=event_source_mapping_id) + event_source_mapping_function_arn = event_source_mapping_result["FunctionArn"] + self.assertEqual(event_source_mapping_function_arn, lambda_function_arn) diff --git a/integration/resources/expected/combination/function_with_self_managed_kafka.json b/integration/resources/expected/combination/function_with_self_managed_kafka.json new file mode 100644 index 000000000..9cf382960 --- /dev/null +++ b/integration/resources/expected/combination/function_with_self_managed_kafka.json @@ -0,0 +1,18 @@ +[ + { + "LogicalResourceId": "KafkaFunction", + "ResourceType": "AWS::Lambda::Function" + }, + { + "LogicalResourceId": "KafkaFunctionMyKafkaCluster", + "ResourceType": "AWS::Lambda::EventSourceMapping" + }, + { + "LogicalResourceId": "KafkaFunctionRole", + "ResourceType": "AWS::IAM::Role" + }, + { + "LogicalResourceId": "KafkaUserSecret", + "ResourceType": "AWS::SecretsManager::Secret" + } +] \ No newline at end of file diff --git a/integration/resources/expected/combination/function_with_self_managed_kafka_intrinsics.json b/integration/resources/expected/combination/function_with_self_managed_kafka_intrinsics.json new file mode 100644 index 000000000..f4d3f90a5 --- /dev/null +++ b/integration/resources/expected/combination/function_with_self_managed_kafka_intrinsics.json @@ -0,0 +1,18 @@ +[ + { + "LogicalResourceId": "KafkaFunctionWithIntrinsics", + "ResourceType": "AWS::Lambda::Function" + }, + { + "LogicalResourceId": "KafkaFunctionWithIntrinsicsMyKafkaClusterWithIntrinsics", + "ResourceType": "AWS::Lambda::EventSourceMapping" + }, + { + "LogicalResourceId": "KafkaFunctionWithIntrinsicsRole", + "ResourceType": "AWS::IAM::Role" + }, + { + "LogicalResourceId": "KafkaUserSecret", + "ResourceType": "AWS::SecretsManager::Secret" + } +] \ No newline at end of file diff --git a/integration/resources/templates/combination/function_with_self_managed_kafka.yaml b/integration/resources/templates/combination/function_with_self_managed_kafka.yaml new file mode 100644 index 000000000..26f3f46f1 --- /dev/null +++ b/integration/resources/templates/combination/function_with_self_managed_kafka.yaml @@ -0,0 +1,29 @@ +Resources: + KafkaFunction: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: nodejs12.x + CodeUri: ${codeuri} + MemorySize: 128 + Events: + MyKafkaCluster: + Type: SelfManagedKafka + Properties: + KafkaBootstrapServers: + - abc.xyz.com:9092 + - 123.45.67.89:9096 + Topics: + - Topic1 + SourceAccessConfigurations: + - Type: BASIC_AUTH + URI: + Ref: KafkaUserSecret + + KafkaUserSecret: + Type: AWS::SecretsManager::Secret + Properties: + Name: KafkaUserPassword + SecretString: + Fn::Sub: '{"username":"testBrokerUser","password":"testBrokerPassword"}' + diff --git a/integration/resources/templates/combination/function_with_self_managed_kafka_intrinsics.yaml b/integration/resources/templates/combination/function_with_self_managed_kafka_intrinsics.yaml new file mode 100644 index 000000000..709850550 --- /dev/null +++ b/integration/resources/templates/combination/function_with_self_managed_kafka_intrinsics.yaml @@ -0,0 +1,37 @@ +Parameters: + TopicsValue: + Type: CommaDelimitedList + Default: Topic + + KafkaBootstrapServersValue: + Type: String + Default: abc.xyz.com:9092 + +Resources: + KafkaFunctionWithIntrinsics: + Type: AWS::Serverless::Function + Properties: + Handler: index.handler + Runtime: nodejs12.x + CodeUri: ${codeuri} + MemorySize: 128 + Events: + MyKafkaClusterWithIntrinsics: + Type: SelfManagedKafka + Properties: + KafkaBootstrapServers: + - Ref: KafkaBootstrapServersValue + Topics: + Ref: TopicsValue + SourceAccessConfigurations: + - Type: BASIC_AUTH + URI: + Ref: KafkaUserSecret + + KafkaUserSecret: + Type: AWS::SecretsManager::Secret + Properties: + Name: KafkaUserPassword + SecretString: + Fn::Sub: '{"username":"testBrokerUserWithInstrinsic","password":"testBrokerPasswordWithInstrinsic"}' + diff --git a/samtranslator/model/eventsources/pull.py b/samtranslator/model/eventsources/pull.py index a88d71fe6..537d9cd30 100644 --- a/samtranslator/model/eventsources/pull.py +++ b/samtranslator/model/eventsources/pull.py @@ -1,7 +1,9 @@ +from six import string_types from samtranslator.metrics.method_decorator import cw_timer from samtranslator.model import ResourceMacro, PropertyType from samtranslator.model.eventsources import FUNCTION_EVETSOURCE_METRIC_PREFIX -from samtranslator.model.types import is_type, is_str +from samtranslator.model.types import is_type, is_str, list_of +from samtranslator.model.intrinsics import is_intrinsic from samtranslator.model.lambda_ import LambdaEventSourceMapping from samtranslator.translator.arn_generator import ArnGenerator @@ -20,6 +22,7 @@ class PullEventSource(ResourceMacro): """ resource_type = None + requires_stream_queue_broker = True property_types = { "Stream": PropertyType(False, is_str()), "Queue": PropertyType(False, is_str()), @@ -39,6 +42,7 @@ class PullEventSource(ResourceMacro): "SecretsManagerKmsKeyId": PropertyType(False, is_str()), "TumblingWindowInSeconds": PropertyType(False, is_type(int)), "FunctionResponseTypes": PropertyType(False, is_type(list)), + "KafkaBootstrapServers": PropertyType(False, is_type(list)), } def get_policy_arn(self): @@ -74,7 +78,7 @@ def to_cloudformation(self, **kwargs): except NotImplementedError: function_name_or_arn = function.get_runtime_attr("arn") - if not self.Stream and not self.Queue and not self.Broker: + if self.requires_stream_queue_broker and not self.Stream and not self.Queue and not self.Broker: raise InvalidEventException( self.relative_id, "No Queue (for SQS) or Stream (for Kinesis, DynamoDB or MSK) or Broker (for Amazon MQ) provided.", @@ -99,6 +103,11 @@ def to_cloudformation(self, **kwargs): lambda_eventsourcemapping.TumblingWindowInSeconds = self.TumblingWindowInSeconds lambda_eventsourcemapping.FunctionResponseTypes = self.FunctionResponseTypes + if self.KafkaBootstrapServers: + lambda_eventsourcemapping.SelfManagedEventSource = { + "Endpoints": {"KafkaBootstrapServers": self.KafkaBootstrapServers} + } + destination_config_policy = None if self.DestinationConfig: # `Type` property is for sam to attach the right policies @@ -286,3 +295,149 @@ def get_policy_statements(self): } document["PolicyDocument"]["Statement"].append(kms_policy) return [document] + + +class SelfManagedKafka(PullEventSource): + """ + SelfManagedKafka event source + """ + + resource_type = "SelfManagedKafka" + requires_stream_queue_broker = False + AUTH_MECHANISM = ["SASL_SCRAM_256_AUTH", "SASL_SCRAM_512_AUTH", "BASIC_AUTH"] + + def get_policy_arn(self): + return None + + def get_policy_statements(self): + if not self.KafkaBootstrapServers: + raise InvalidEventException( + self.relative_id, + "No KafkaBootstrapServers provided for self managed kafka as an event source", + ) + + if not self.Topics: + raise InvalidEventException( + self.relative_id, + "No Topics provided for self managed kafka as an event source", + ) + + if len(self.Topics) != 1: + raise InvalidEventException( + self.relative_id, + "Topics for self managed kafka only supports single configuration entry.", + ) + + if not self.SourceAccessConfigurations: + raise InvalidEventException( + self.relative_id, + "No SourceAccessConfigurations for self managed kafka event provided.", + ) + document = self.generate_policy_document() + return [document] + + def generate_policy_document(self): + statements = [] + authentication_uri, has_vpc_config = self.get_secret_key() + if authentication_uri: + secret_manager = self.get_secret_manager_secret(authentication_uri) + statements.append(secret_manager) + + if has_vpc_config: + vpc_permissions = self.get_vpc_permission() + statements.append(vpc_permissions) + + if self.SecretsManagerKmsKeyId: + kms_policy = self.get_kms_policy() + statements.append(kms_policy) + + document = { + "PolicyDocument": { + "Statement": statements, + "Version": "2012-10-17", + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy", + } + + return document + + def get_secret_key(self): + authentication_uri = None + has_vpc_subnet = False + has_vpc_security_group = False + for config in self.SourceAccessConfigurations: + if config.get("Type") == "VPC_SUBNET": + self.validate_uri(config, "VPC_SUBNET") + has_vpc_subnet = True + + elif config.get("Type") == "VPC_SECURITY_GROUP": + self.validate_uri(config, "VPC_SECURITY_GROUP") + has_vpc_security_group = True + + elif config.get("Type") in self.AUTH_MECHANISM: + if authentication_uri: + raise InvalidEventException( + self.relative_id, + "Multiple auth mechanism properties specified in SourceAccessConfigurations for self managed kafka event.", + ) + self.validate_uri(config, "auth mechanism") + authentication_uri = config.get("URI") + + else: + raise InvalidEventException( + self.relative_id, + "Invalid SourceAccessConfigurations Type provided for self managed kafka event.", + ) + + if (not has_vpc_subnet and has_vpc_security_group) or (has_vpc_subnet and not has_vpc_security_group): + raise InvalidEventException( + self.relative_id, + "VPC_SUBNET and VPC_SECURITY_GROUP in SourceAccessConfigurations for SelfManagedKafka must be both provided.", + ) + return authentication_uri, (has_vpc_subnet and has_vpc_security_group) + + def validate_uri(self, config, msg): + if not config.get("URI"): + raise InvalidEventException( + self.relative_id, + "No {} URI property specified in SourceAccessConfigurations for self managed kafka event.".format(msg), + ) + + if not isinstance(config.get("URI"), string_types) and not is_intrinsic(config.get("URI")): + raise InvalidEventException( + self.relative_id, + "Wrong Type for {} URI property specified in SourceAccessConfigurations for self managed kafka event.".format( + msg + ), + ) + + def get_secret_manager_secret(self, authentication_uri): + return { + "Action": ["secretsmanager:GetSecretValue"], + "Effect": "Allow", + "Resource": authentication_uri, + } + + def get_vpc_permission(self): + return { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + ], + "Effect": "Allow", + "Resource": "*", + } + + def get_kms_policy(self): + return { + "Action": ["kms:Decrypt"], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/" + + self.SecretsManagerKmsKeyId + }, + } diff --git a/samtranslator/model/lambda_.py b/samtranslator/model/lambda_.py index 310c5c55e..75089bc04 100644 --- a/samtranslator/model/lambda_.py +++ b/samtranslator/model/lambda_.py @@ -64,7 +64,7 @@ class LambdaEventSourceMapping(Resource): property_types = { "BatchSize": PropertyType(False, is_type(int)), "Enabled": PropertyType(False, is_type(bool)), - "EventSourceArn": PropertyType(True, is_str()), + "EventSourceArn": PropertyType(False, is_str()), "FunctionName": PropertyType(True, is_str()), "MaximumBatchingWindowInSeconds": PropertyType(False, is_type(int)), "MaximumRetryAttempts": PropertyType(False, is_type(int)), @@ -78,6 +78,7 @@ class LambdaEventSourceMapping(Resource): "SourceAccessConfigurations": PropertyType(False, is_type(list)), "TumblingWindowInSeconds": PropertyType(False, is_type(int)), "FunctionResponseTypes": PropertyType(False, is_type(list)), + "SelfManagedEventSource": PropertyType(False, is_type(dict)), } runtime_attrs = {"name": lambda self: ref(self.logical_id)} diff --git a/tests/model/eventsources/test_self_managed_kafka_event_source.py b/tests/model/eventsources/test_self_managed_kafka_event_source.py new file mode 100644 index 000000000..1c909d4fc --- /dev/null +++ b/tests/model/eventsources/test_self_managed_kafka_event_source.py @@ -0,0 +1,296 @@ +from unittest import TestCase +from samtranslator.model.eventsources.pull import SelfManagedKafka +from samtranslator.model.exceptions import InvalidEventException + + +class SelfManagedKafkaEventSource(TestCase): + def setUp(self): + self.logical_id = "SelfManagedKafkaEvent" + self.kafka_event_source = SelfManagedKafka(self.logical_id) + + def test_get_policy_arn(self): + arn = self.kafka_event_source.get_policy_arn() + expected_arn = None + self.assertEqual(arn, expected_arn) + + def test_get_policy_statements(self): + self.kafka_event_source.SourceAccessConfigurations = [ + {"Type": "SASL_SCRAM_256_AUTH", "URI": "SECRET_URI"}, + {"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, + {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}, + ] + self.kafka_event_source.Topics = ["Topics"] + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.BatchSize = 1 + + policy_statements = self.kafka_event_source.get_policy_statements() + expected_policy_document = [ + { + "PolicyDocument": { + "Statement": [ + {"Action": ["secretsmanager:GetSecretValue"], "Effect": "Allow", "Resource": "SECRET_URI"}, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + ], + "Effect": "Allow", + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy", + } + ] + + self.assertEqual(policy_statements, expected_policy_document) + + def test_get_policy_statements_with_only_auth_mechanism(self): + self.kafka_event_source.SourceAccessConfigurations = [ + {"Type": "BASIC_AUTH", "URI": "SECRET_URI"}, + ] + self.kafka_event_source.Topics = ["Topics"] + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.BatchSize = 1 + + policy_statements = self.kafka_event_source.get_policy_statements() + expected_policy_document = [ + { + "PolicyDocument": { + "Statement": [ + {"Action": ["secretsmanager:GetSecretValue"], "Effect": "Allow", "Resource": "SECRET_URI"}, + ], + "Version": "2012-10-17", + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy", + } + ] + + self.assertEqual(policy_statements, expected_policy_document) + + def test_get_policy_statements_with_only_vpc_config(self): + self.kafka_event_source.SourceAccessConfigurations = [ + {"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, + {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}, + ] + self.kafka_event_source.Topics = ["Topics"] + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.BatchSize = 1 + + policy_statements = self.kafka_event_source.get_policy_statements() + expected_policy_document = [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + ], + "Effect": "Allow", + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy", + } + ] + self.assertEqual(policy_statements, expected_policy_document) + + def test_get_policy_statements_with_secrets_manager_kms_key_id(self): + self.kafka_event_source.SourceAccessConfigurations = [ + {"Type": "SASL_SCRAM_256_AUTH", "URI": "SECRET_URI"}, + {"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, + {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}, + ] + self.kafka_event_source.Topics = ["Topics"] + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.BatchSize = 1 + self.kafka_event_source.SecretsManagerKmsKeyId = "SECRET_KEY" + + policy_statements = self.kafka_event_source.get_policy_statements() + expected_policy_document = [ + { + "PolicyDocument": { + "Statement": [ + {"Action": ["secretsmanager:GetSecretValue"], "Effect": "Allow", "Resource": "SECRET_URI"}, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups", + ], + "Effect": "Allow", + "Resource": "*", + }, + { + "Action": ["kms:Decrypt"], + "Effect": "Allow", + "Resource": { + "Fn::Sub": "arn:${AWS::Partition}:kms:${AWS::Region}:${AWS::AccountId}:key/" + + self.kafka_event_source.SecretsManagerKmsKeyId + }, + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy", + } + ] + self.assertEqual(policy_statements, expected_policy_document) + + def test_must_raise_for_missing_topics(self): + self.kafka_event_source.SourceAccessConfigurations = [ + {"Type": "SASL_SCRAM_256_AUTH", "URI": "SECRET_URI"}, + {"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, + {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}, + ] + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.BatchSize = 1 + + with self.assertRaises(InvalidEventException): + self.kafka_event_source.get_policy_statements() + + def test_must_raise_for_empty_topics(self): + self.kafka_event_source.SourceAccessConfigurations = [ + {"Type": "SASL_SCRAM_256_AUTH", "URI": "SECRET_URI"}, + {"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, + {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}, + ] + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.BatchSize = 1 + self.kafka_event_source.Topics = [] + + with self.assertRaises(InvalidEventException): + self.kafka_event_source.get_policy_statements() + + def test_must_raise_for_multiple_topics(self): + self.kafka_event_source.SourceAccessConfigurations = [ + {"Type": "SASL_SCRAM_256_AUTH", "URI": "SECRET_URI"}, + {"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, + {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}, + ] + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Topics = ["Topics1", "Topics2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.BatchSize = 1 + + with self.assertRaises(InvalidEventException): + self.kafka_event_source.get_policy_statements() + + def test_must_raise_for_missing_endpoints(self): + self.kafka_event_source.SourceAccessConfigurations = [ + {"Type": "SASL_SCRAM_256_AUTH", "URI": "SECRET_URI"}, + {"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, + {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}, + ] + self.kafka_event_source.Enabled = True + self.kafka_event_source.Topics = ["Topics"] + self.kafka_event_source.BatchSize = 1 + + with self.assertRaises(InvalidEventException): + self.kafka_event_source.get_policy_statements() + + def test_must_raise_for_empty_bootstrap_server(self): + self.kafka_event_source.SourceAccessConfigurations = [ + {"Type": "SASL_SCRAM_256_AUTH", "URI": "SECRET_URI"}, + {"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, + {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}, + ] + self.kafka_event_source.KafkaBootstrapServers = [] + self.kafka_event_source.Enabled = True + self.kafka_event_source.Topics = ["Topics"] + self.kafka_event_source.BatchSize = 1 + + with self.assertRaises(InvalidEventException): + self.kafka_event_source.get_policy_statements() + + def test_must_raise_for_missing_vpc_subnet(self): + self.kafka_event_source.SourceAccessConfigurations = [ + {"Type": "SASL_SCRAM_256_AUTH", "URI": "SECRET_URI"}, + {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}, + ] + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.Topics = ["Topics"] + self.kafka_event_source.BatchSize = 1 + + with self.assertRaises(InvalidEventException): + self.kafka_event_source.get_policy_statements() + + def test_must_raise_for_missing_vpc_security_group(self): + self.kafka_event_source.SourceAccessConfigurations = [ + {"Type": "SASL_SCRAM_256_AUTH", "URI": "SECRET_URI"}, + {"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, + ] + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.Topics = ["Topics"] + self.kafka_event_source.BatchSize = 1 + + with self.assertRaises(InvalidEventException): + self.kafka_event_source.get_policy_statements() + + def test_must_raise_for_missing_source_access_configurations(self): + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.Topics = ["Topics"] + self.kafka_event_source.BatchSize = 1 + + with self.assertRaises(InvalidEventException): + self.kafka_event_source.get_policy_statements() + + def test_must_raise_for_unknown_source_access_configurations_type(self): + test_credentials = [ + [{"Type": "BASIC_AUT", "URI": "SECRET_URI"}], + [{"Type": "SASL_SCRAM_256_AUT", "URI": "SECRET_URI"}], + [{"Type": None, "URI": "SECRET_URI"}], + [{"Type": "VPC_SUB", "URI": "SECRET_URI"}, {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}], + [{"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, {"Type": None, "URI": None}], + ] + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.Topics = ["Topics"] + self.kafka_event_source.BatchSize = 1 + + for config in test_credentials: + self.kafka_event_source.SourceAccessConfigurations = config + with self.assertRaises(InvalidEventException): + self.kafka_event_source.get_policy_statements() + + def test_must_raise_for_wrong_source_access_configurations_uri(self): + test_credentials = [ + [{"Type": "BASIC_AUTH", "URI": 1}], + [{"Type": "SASL_SCRAM_256_AUTH", "URI": 1}], + [{"Type": "SASL_SCRAM_512_AUTH", "URI": 1}], + [{"Type": "VPC_SUBNET", "URI": None}, {"Type": "VPC_SECURITY_GROUP", "URI": "SECRET_URI"}], + [{"Type": "VPC_SUBNET", "URI": "SECRET_URI"}, {"Type": "VPC_SECURITY_GROUP", "URI": None}], + ] + self.kafka_event_source.KafkaBootstrapServers = ["endpoint1", "endpoint2"] + self.kafka_event_source.Enabled = True + self.kafka_event_source.Topics = ["Topics"] + self.kafka_event_source.BatchSize = 1 + + for config in test_credentials: + self.kafka_event_source.SourceAccessConfigurations = config + with self.assertRaises(InvalidEventException): + self.kafka_event_source.get_policy_statements() diff --git a/tests/model/test_api_v2.py b/tests/model/test_api_v2.py index 22b62aabe..7299f6ba0 100644 --- a/tests/model/test_api_v2.py +++ b/tests/model/test_api_v2.py @@ -15,11 +15,11 @@ def test_create_oauth2_auth(self): id_source="https://example.com", authorization_scopes=["scope1", "scope2"], ) - self.assertEquals(auth.api_logical_id, "logicalId") - self.assertEquals(auth.name, "authName") - self.assertEquals(auth.jwt_configuration, {"config": "value"}) - self.assertEquals(auth.id_source, "https://example.com") - self.assertEquals(auth.authorization_scopes, ["scope1", "scope2"]) + self.assertEqual(auth.api_logical_id, "logicalId") + self.assertEqual(auth.name, "authName") + self.assertEqual(auth.jwt_configuration, {"config": "value"}) + self.assertEqual(auth.id_source, "https://example.com") + self.assertEqual(auth.authorization_scopes, ["scope1", "scope2"]) def test_create_lambda_auth(self): auth = ApiGatewayV2Authorizer( @@ -31,12 +31,12 @@ def test_create_lambda_auth(self): authorizer_payload_format_version="2.0", enable_simple_responses=True, ) - self.assertEquals(auth.api_logical_id, "logicalId") - self.assertEquals(auth.name, "lambdaAuth") - self.assertEquals(auth.function_arn, "lambdaArn") - self.assertEquals(auth.identity, {"Headers": ["Authorization"], "ReauthorizeEvery": 42}) - self.assertEquals(auth.authorizer_payload_format_version, "2.0") - self.assertEquals(auth.enable_simple_responses, True) + self.assertEqual(auth.api_logical_id, "logicalId") + self.assertEqual(auth.name, "lambdaAuth") + self.assertEqual(auth.function_arn, "lambdaArn") + self.assertEqual(auth.identity, {"Headers": ["Authorization"], "ReauthorizeEvery": 42}) + self.assertEqual(auth.authorizer_payload_format_version, "2.0") + self.assertEqual(auth.enable_simple_responses, True) def test_create_authorizer_fails_with_string_authorization_scopes(self): with pytest.raises(InvalidResourceException) as e: diff --git a/tests/translator/input/error_invalid_self_managed_kafka_config.yaml b/tests/translator/input/error_invalid_self_managed_kafka_config.yaml new file mode 100644 index 000000000..2c49fb46e --- /dev/null +++ b/tests/translator/input/error_invalid_self_managed_kafka_config.yaml @@ -0,0 +1,24 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: {} +Resources: + KafkaFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/kafka.zip + Handler: index.kafka_handler + Runtime: python3.9 + Events: + MyKafkaCluster: + Type: SelfManagedKafka + Properties: + KafkaBootstrapServers: + - "abc.xyz.com:9092" + - "123.45.67.89:9096" + Topics: + - "Topic1" + SourceAccessConfigurations: + - Type: SASL_SCRAM_512_AUTH + URI: arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + - Type: VPC_SUBNET + URI: subnet:subnet-12345 + diff --git a/tests/translator/input/error_missing_kafka_bootstrap_server_for_self_managed_kafka.yaml b/tests/translator/input/error_missing_kafka_bootstrap_server_for_self_managed_kafka.yaml new file mode 100644 index 000000000..4e3027c85 --- /dev/null +++ b/tests/translator/input/error_missing_kafka_bootstrap_server_for_self_managed_kafka.yaml @@ -0,0 +1,23 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: {} +Resources: + KafkaFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/kafka.zip + Handler: index.kafka_handler + Runtime: python3.9 + Events: + MyKafkaCluster: + Type: SelfManagedKafka + Properties: + Topics: + - "Topic1" + SourceAccessConfigurations: + - Type: SASL_SCRAM_512_AUTH + URI: arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + - Type: VPC_SUBNET + URI: subnet:subnet-12345 + - Type: VPC_SECURITY_GROUP + URI: security_group:sg-67890 + diff --git a/tests/translator/input/error_missing_source_access_configurations_for_self_managed_kafka.yaml b/tests/translator/input/error_missing_source_access_configurations_for_self_managed_kafka.yaml new file mode 100644 index 000000000..a3cba88e2 --- /dev/null +++ b/tests/translator/input/error_missing_source_access_configurations_for_self_managed_kafka.yaml @@ -0,0 +1,19 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: {} +Resources: + KafkaFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/kafka.zip + Handler: index.kafka_handler + Runtime: python3.9 + Events: + MyKafkaCluster: + Type: SelfManagedKafka + Properties: + KafkaBootstrapServers: + - "abc.xyz.com:9092" + - "123.45.67.89:9096" + Topics: + - "Topic1" + diff --git a/tests/translator/input/error_missing_topics_for_self_managed_kafka.yaml b/tests/translator/input/error_missing_topics_for_self_managed_kafka.yaml new file mode 100644 index 000000000..c01e896d8 --- /dev/null +++ b/tests/translator/input/error_missing_topics_for_self_managed_kafka.yaml @@ -0,0 +1,24 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: {} +Resources: + KafkaFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/kafka.zip + Handler: index.kafka_handler + Runtime: python3.9 + Events: + MyKafkaCluster: + Type: SelfManagedKafka + Properties: + KafkaBootstrapServers: + - "abc.xyz.com:9092" + - "123.45.67.89:9096" + SourceAccessConfigurations: + - Type: SASL_SCRAM_512_AUTH + URI: arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + - Type: VPC_SUBNET + URI: subnet:subnet-12345 + - Type: VPC_SECURITY_GROUP + URI: security_group:sg-67890 + diff --git a/tests/translator/input/error_multiple_auth_mechanism_self_managed_kafka_config.yaml b/tests/translator/input/error_multiple_auth_mechanism_self_managed_kafka_config.yaml new file mode 100644 index 000000000..9e196280c --- /dev/null +++ b/tests/translator/input/error_multiple_auth_mechanism_self_managed_kafka_config.yaml @@ -0,0 +1,25 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: {} +Resources: + KafkaFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/kafka.zip + Handler: index.kafka_handler + Runtime: python3.9 + Events: + MyKafkaCluster: + Type: SelfManagedKafka + Properties: + KafkaBootstrapServers: + - "abc.xyz.com:9092" + - "123.45.67.89:9096" + Topics: + - "Topic1" + SourceAccessConfigurations: + - Type: SASL_SCRAM_512_AUTH + URI: arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + - Type: BASIC_AUTH + URI: arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + + diff --git a/tests/translator/input/error_multiple_topics_for_self_managed_kafka.yaml b/tests/translator/input/error_multiple_topics_for_self_managed_kafka.yaml new file mode 100644 index 000000000..07a80011b --- /dev/null +++ b/tests/translator/input/error_multiple_topics_for_self_managed_kafka.yaml @@ -0,0 +1,25 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: {} +Resources: + KafkaFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/kafka.zip + Handler: index.kafka_handler + Runtime: python3.9 + Events: + MyKafkaCluster: + Type: SelfManagedKafka + Properties: + KafkaBootstrapServers: + - "abc.xyz.com:9092" + - "123.45.67.89:9096" + Topics: + - "Topic1" + - "Topic2" + SourceAccessConfigurations: + - Type: VPC_SUBNET + URI: subnet:subnet-12345 + - Type: VPC_SECURITY_GROUP + URI: security_group:sg-67890 + diff --git a/tests/translator/input/function_with_auth_mechanism_for_self_managed_kafka.yaml b/tests/translator/input/function_with_auth_mechanism_for_self_managed_kafka.yaml new file mode 100644 index 000000000..504fd44c0 --- /dev/null +++ b/tests/translator/input/function_with_auth_mechanism_for_self_managed_kafka.yaml @@ -0,0 +1,22 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: {} +Resources: + KafkaFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/kafka.zip + Handler: index.kafka_handler + Runtime: python3.9 + Events: + MyKafkaCluster: + Type: SelfManagedKafka + Properties: + KafkaBootstrapServers: + - "abc.xyz.com:9092" + - "123.45.67.89:9096" + Topics: + - "Topic1" + SourceAccessConfigurations: + - Type: BASIC_AUTH + URI: arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + diff --git a/tests/translator/input/function_with_self_managed_kafka.yaml b/tests/translator/input/function_with_self_managed_kafka.yaml new file mode 100644 index 000000000..a5ed1dfaf --- /dev/null +++ b/tests/translator/input/function_with_self_managed_kafka.yaml @@ -0,0 +1,26 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: {} +Resources: + KafkaFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/kafka.zip + Handler: index.kafka_handler + Runtime: python3.9 + Events: + MyKafkaCluster: + Type: SelfManagedKafka + Properties: + KafkaBootstrapServers: + - "abc.xyz.com:9092" + - "123.45.67.89:9096" + Topics: + - "Topic1" + SourceAccessConfigurations: + - Type: SASL_SCRAM_512_AUTH + URI: arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c + - Type: VPC_SUBNET + URI: subnet:subnet-12345 + - Type: VPC_SECURITY_GROUP + URI: security_group:sg-67890 + diff --git a/tests/translator/input/function_with_vpc_permission_for_self_managed_kafka.yaml b/tests/translator/input/function_with_vpc_permission_for_self_managed_kafka.yaml new file mode 100644 index 000000000..fe231e32c --- /dev/null +++ b/tests/translator/input/function_with_vpc_permission_for_self_managed_kafka.yaml @@ -0,0 +1,24 @@ +AWSTemplateFormatVersion: '2010-09-09' +Parameters: {} +Resources: + KafkaFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/kafka.zip + Handler: index.kafka_handler + Runtime: python3.9 + Events: + MyKafkaCluster: + Type: SelfManagedKafka + Properties: + KafkaBootstrapServers: + - "abc.xyz.com:9092" + - "123.45.67.89:9096" + Topics: + - "Topic1" + SourceAccessConfigurations: + - Type: VPC_SUBNET + URI: subnet:subnet-12345 + - Type: VPC_SECURITY_GROUP + URI: security_group:sg-67890 + diff --git a/tests/translator/input/self_managed_kafka_with_intrinsics.yaml b/tests/translator/input/self_managed_kafka_with_intrinsics.yaml new file mode 100644 index 000000000..5a0f3b380 --- /dev/null +++ b/tests/translator/input/self_managed_kafka_with_intrinsics.yaml @@ -0,0 +1,53 @@ +Parameters: + BatchSizeValue: + Type: Number + Default: 100 + + EnableValue: + Type: String + Default: True + + TopicsValue: + Type: CommaDelimitedList + Default: Topic + + KafkaBootstrapServersValue: + Type: CommaDelimitedList + Default: abc.xyz.com:9092,123.45.67.89:9096 + + +Resources: + KafkaFunction: + Type: 'AWS::Serverless::Function' + Properties: + CodeUri: s3://sam-demo-bucket/kafka.zip + Handler: index.kafka_handler + Runtime: python3.9 + Events: + MyKafkaCluster: + Type: SelfManagedKafka + Properties: + KafkaBootstrapServers: + Ref: KafkaBootstrapServersValue + Topics: + Ref: TopicsValue + Enabled: + Ref: EnableValue + BatchSize: + Ref: BatchSizeValue + SourceAccessConfigurations: + - Type: VPC_SUBNET + URI: subnet:subnet-12345 + - Type: VPC_SECURITY_GROUP + URI: security_group:sg-67890 + - Type: BASIC_AUTH + URI: + Ref: KafkaUserSecret + + KafkaUserSecret: + Type: AWS::SecretsManager::Secret + Properties: + Name: KafkaUserPassword + SecretString: + Fn::Sub: '{"username":"testBrokerUser","password":"testBrokerPassword"}' + diff --git a/tests/translator/output/aws-cn/function_with_auth_mechanism_for_self_managed_kafka.json b/tests/translator/output/aws-cn/function_with_auth_mechanism_for_self_managed_kafka.json new file mode 100644 index 000000000..b446ea878 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_auth_mechanism_for_self_managed_kafka.json @@ -0,0 +1,101 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Parameters": {}, + "Resources": { + "KafkaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.kafka_handler", + "Role": { + "Fn::GetAtt": [ + "KafkaFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionMyKafkaCluster": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "FunctionName": { + "Ref": "KafkaFunction" + }, + "Topics": [ + "Topic1" + ], + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + ], + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": [ + "abc.xyz.com:9092", + "123.45.67.89:9096" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/function_with_self_managed_kafka.json b/tests/translator/output/aws-cn/function_with_self_managed_kafka.json new file mode 100644 index 000000000..9c0ff439a --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_self_managed_kafka.json @@ -0,0 +1,121 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Parameters": {}, + "Resources": { + "KafkaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.kafka_handler", + "Role": { + "Fn::GetAtt": [ + "KafkaFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionMyKafkaCluster": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "FunctionName": { + "Ref": "KafkaFunction" + }, + "Topics": [ + "Topic1" + ], + "SourceAccessConfigurations": [ + { + "Type": "SASL_SCRAM_512_AUTH", + "URI": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + }, + { + "Type": "VPC_SUBNET", + "URI": "subnet:subnet-12345" + }, + { + "Type": "VPC_SECURITY_GROUP", + "URI": "security_group:sg-67890" + } + ], + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": [ + "abc.xyz.com:9092", + "123.45.67.89:9096" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/function_with_vpc_permission_for_self_managed_kafka.json b/tests/translator/output/aws-cn/function_with_vpc_permission_for_self_managed_kafka.json new file mode 100644 index 000000000..ca6ec6013 --- /dev/null +++ b/tests/translator/output/aws-cn/function_with_vpc_permission_for_self_managed_kafka.json @@ -0,0 +1,110 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Parameters": {}, + "Resources": { + "KafkaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.kafka_handler", + "Role": { + "Fn::GetAtt": [ + "KafkaFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionMyKafkaCluster": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "FunctionName": { + "Ref": "KafkaFunction" + }, + "Topics": [ + "Topic1" + ], + "SourceAccessConfigurations": [ + { + "Type": "VPC_SUBNET", + "URI": "subnet:subnet-12345" + }, + { + "Type": "VPC_SECURITY_GROUP", + "URI": "security_group:sg-67890" + } + ], + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": [ + "abc.xyz.com:9092", + "123.45.67.89:9096" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-cn/self_managed_kafka_with_intrinsics.json b/tests/translator/output/aws-cn/self_managed_kafka_with_intrinsics.json new file mode 100644 index 000000000..75743f1bd --- /dev/null +++ b/tests/translator/output/aws-cn/self_managed_kafka_with_intrinsics.json @@ -0,0 +1,155 @@ +{ + "Parameters": { + "BatchSizeValue": { + "Type": "Number", + "Default": 100 + }, + "EnableValue": { + "Type": "String", + "Default": true + }, + "TopicsValue": { + "Type": "CommaDelimitedList", + "Default": "Topic" + }, + "KafkaBootstrapServersValue": { + "Type": "CommaDelimitedList", + "Default": "abc.xyz.com:9092,123.45.67.89:9096" + } + }, + "Resources": { + "KafkaUserSecret": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "Name": "KafkaUserPassword", + "SecretString": { + "Fn::Sub": "{\"username\":\"testBrokerUser\",\"password\":\"testBrokerPassword\"}" + } + } + }, + "KafkaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.kafka_handler", + "Role": { + "Fn::GetAtt": [ + "KafkaFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-cn:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Ref": "KafkaUserSecret" + } + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionMyKafkaCluster": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": { + "Ref": "BatchSizeValue" + }, + "Enabled": { + "Ref": "EnableValue" + }, + "FunctionName": { + "Ref": "KafkaFunction" + }, + "Topics": { + "Ref": "TopicsValue" + }, + "SourceAccessConfigurations": [ + { + "Type": "VPC_SUBNET", + "URI": "subnet:subnet-12345" + }, + { + "Type": "VPC_SECURITY_GROUP", + "URI": "security_group:sg-67890" + }, + { + "Type": "BASIC_AUTH", + "URI": { + "Ref": "KafkaUserSecret" + } + } + ], + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": { + "Ref": "KafkaBootstrapServersValue" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_auth_mechanism_for_self_managed_kafka.json b/tests/translator/output/aws-us-gov/function_with_auth_mechanism_for_self_managed_kafka.json new file mode 100644 index 000000000..dd15eecf3 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_auth_mechanism_for_self_managed_kafka.json @@ -0,0 +1,101 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Parameters": {}, + "Resources": { + "KafkaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.kafka_handler", + "Role": { + "Fn::GetAtt": [ + "KafkaFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionMyKafkaCluster": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "FunctionName": { + "Ref": "KafkaFunction" + }, + "Topics": [ + "Topic1" + ], + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + ], + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": [ + "abc.xyz.com:9092", + "123.45.67.89:9096" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_self_managed_kafka.json b/tests/translator/output/aws-us-gov/function_with_self_managed_kafka.json new file mode 100644 index 000000000..8b62e5d04 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_self_managed_kafka.json @@ -0,0 +1,121 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Parameters": {}, + "Resources": { + "KafkaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.kafka_handler", + "Role": { + "Fn::GetAtt": [ + "KafkaFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionMyKafkaCluster": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "FunctionName": { + "Ref": "KafkaFunction" + }, + "Topics": [ + "Topic1" + ], + "SourceAccessConfigurations": [ + { + "Type": "SASL_SCRAM_512_AUTH", + "URI": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + }, + { + "Type": "VPC_SUBNET", + "URI": "subnet:subnet-12345" + }, + { + "Type": "VPC_SECURITY_GROUP", + "URI": "security_group:sg-67890" + } + ], + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": [ + "abc.xyz.com:9092", + "123.45.67.89:9096" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/function_with_vpc_permission_for_self_managed_kafka.json b/tests/translator/output/aws-us-gov/function_with_vpc_permission_for_self_managed_kafka.json new file mode 100644 index 000000000..3316f1648 --- /dev/null +++ b/tests/translator/output/aws-us-gov/function_with_vpc_permission_for_self_managed_kafka.json @@ -0,0 +1,110 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Parameters": {}, + "Resources": { + "KafkaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.kafka_handler", + "Role": { + "Fn::GetAtt": [ + "KafkaFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionMyKafkaCluster": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "FunctionName": { + "Ref": "KafkaFunction" + }, + "Topics": [ + "Topic1" + ], + "SourceAccessConfigurations": [ + { + "Type": "VPC_SUBNET", + "URI": "subnet:subnet-12345" + }, + { + "Type": "VPC_SECURITY_GROUP", + "URI": "security_group:sg-67890" + } + ], + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": [ + "abc.xyz.com:9092", + "123.45.67.89:9096" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/aws-us-gov/self_managed_kafka_with_intrinsics.json b/tests/translator/output/aws-us-gov/self_managed_kafka_with_intrinsics.json new file mode 100644 index 000000000..e9725ebe6 --- /dev/null +++ b/tests/translator/output/aws-us-gov/self_managed_kafka_with_intrinsics.json @@ -0,0 +1,155 @@ +{ + "Parameters": { + "BatchSizeValue": { + "Type": "Number", + "Default": 100 + }, + "EnableValue": { + "Type": "String", + "Default": true + }, + "TopicsValue": { + "Type": "CommaDelimitedList", + "Default": "Topic" + }, + "KafkaBootstrapServersValue": { + "Type": "CommaDelimitedList", + "Default": "abc.xyz.com:9092,123.45.67.89:9096" + } + }, + "Resources": { + "KafkaUserSecret": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "Name": "KafkaUserPassword", + "SecretString": { + "Fn::Sub": "{\"username\":\"testBrokerUser\",\"password\":\"testBrokerPassword\"}" + } + } + }, + "KafkaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.kafka_handler", + "Role": { + "Fn::GetAtt": [ + "KafkaFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws-us-gov:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Ref": "KafkaUserSecret" + } + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionMyKafkaCluster": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": { + "Ref": "BatchSizeValue" + }, + "Enabled": { + "Ref": "EnableValue" + }, + "FunctionName": { + "Ref": "KafkaFunction" + }, + "Topics": { + "Ref": "TopicsValue" + }, + "SourceAccessConfigurations": [ + { + "Type": "VPC_SUBNET", + "URI": "subnet:subnet-12345" + }, + { + "Type": "VPC_SECURITY_GROUP", + "URI": "security_group:sg-67890" + }, + { + "Type": "BASIC_AUTH", + "URI": { + "Ref": "KafkaUserSecret" + } + } + ], + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": { + "Ref": "KafkaBootstrapServersValue" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/error_invalid_self_managed_kafka_config.json b/tests/translator/output/error_invalid_self_managed_kafka_config.json new file mode 100644 index 000000000..fe7fe49bd --- /dev/null +++ b/tests/translator/output/error_invalid_self_managed_kafka_config.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. VPC_SUBNET and VPC_SECURITY_GROUP in SourceAccessConfigurations for SelfManagedKafka must be both provided." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. VPC_SUBNET and VPC_SECURITY_GROUP in SourceAccessConfigurations for SelfManagedKafka must be both provided." +} \ No newline at end of file diff --git a/tests/translator/output/error_missing_kafka_bootstrap_server_for_self_managed_kafka.json b/tests/translator/output/error_missing_kafka_bootstrap_server_for_self_managed_kafka.json new file mode 100644 index 000000000..834b7b971 --- /dev/null +++ b/tests/translator/output/error_missing_kafka_bootstrap_server_for_self_managed_kafka.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. No KafkaBootstrapServers provided for self managed kafka as an event source" + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. No KafkaBootstrapServers provided for self managed kafka as an event source" +} \ No newline at end of file diff --git a/tests/translator/output/error_missing_source_access_configurations_for_self_managed_kafka.json b/tests/translator/output/error_missing_source_access_configurations_for_self_managed_kafka.json new file mode 100644 index 000000000..7176ccc2b --- /dev/null +++ b/tests/translator/output/error_missing_source_access_configurations_for_self_managed_kafka.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. No SourceAccessConfigurations for self managed kafka event provided." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. No SourceAccessConfigurations for self managed kafka event provided." +} \ No newline at end of file diff --git a/tests/translator/output/error_missing_topics_for_self_managed_kafka.json b/tests/translator/output/error_missing_topics_for_self_managed_kafka.json new file mode 100644 index 000000000..8aceb3a0a --- /dev/null +++ b/tests/translator/output/error_missing_topics_for_self_managed_kafka.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. No Topics provided for self managed kafka as an event source" + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. No Topics provided for self managed kafka as an event source" +} \ No newline at end of file diff --git a/tests/translator/output/error_multiple_auth_mechanism_self_managed_kafka_config.json b/tests/translator/output/error_multiple_auth_mechanism_self_managed_kafka_config.json new file mode 100644 index 000000000..bc3da8797 --- /dev/null +++ b/tests/translator/output/error_multiple_auth_mechanism_self_managed_kafka_config.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. Multiple auth mechanism properties specified in SourceAccessConfigurations for self managed kafka event." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. Multiple auth mechanism properties specified in SourceAccessConfigurations for self managed kafka event." +} \ No newline at end of file diff --git a/tests/translator/output/error_multiple_topics_for_self_managed_kafka.json b/tests/translator/output/error_multiple_topics_for_self_managed_kafka.json new file mode 100644 index 000000000..e2d372be2 --- /dev/null +++ b/tests/translator/output/error_multiple_topics_for_self_managed_kafka.json @@ -0,0 +1,8 @@ +{ + "errors": [ + { + "errorMessage": "Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. Topics for self managed kafka only supports single configuration entry." + } + ], + "errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [KafkaFunction] is invalid. Event with id [MyKafkaCluster] is invalid. Topics for self managed kafka only supports single configuration entry." +} \ No newline at end of file diff --git a/tests/translator/output/function_with_auth_mechanism_for_self_managed_kafka.json b/tests/translator/output/function_with_auth_mechanism_for_self_managed_kafka.json new file mode 100644 index 000000000..30dea8f77 --- /dev/null +++ b/tests/translator/output/function_with_auth_mechanism_for_self_managed_kafka.json @@ -0,0 +1,101 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Parameters": {}, + "Resources": { + "KafkaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.kafka_handler", + "Role": { + "Fn::GetAtt": [ + "KafkaFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionMyKafkaCluster": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "FunctionName": { + "Ref": "KafkaFunction" + }, + "Topics": [ + "Topic1" + ], + "SourceAccessConfigurations": [ + { + "Type": "BASIC_AUTH", + "URI": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + } + ], + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": [ + "abc.xyz.com:9092", + "123.45.67.89:9096" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/function_with_self_managed_kafka.json b/tests/translator/output/function_with_self_managed_kafka.json new file mode 100644 index 000000000..6cb7ae49d --- /dev/null +++ b/tests/translator/output/function_with_self_managed_kafka.json @@ -0,0 +1,121 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Parameters": {}, + "Resources": { + "KafkaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.kafka_handler", + "Role": { + "Fn::GetAtt": [ + "KafkaFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionMyKafkaCluster": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "FunctionName": { + "Ref": "KafkaFunction" + }, + "Topics": [ + "Topic1" + ], + "SourceAccessConfigurations": [ + { + "Type": "SASL_SCRAM_512_AUTH", + "URI": "arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c" + }, + { + "Type": "VPC_SUBNET", + "URI": "subnet:subnet-12345" + }, + { + "Type": "VPC_SECURITY_GROUP", + "URI": "security_group:sg-67890" + } + ], + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": [ + "abc.xyz.com:9092", + "123.45.67.89:9096" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/function_with_vpc_permission_for_self_managed_kafka.json b/tests/translator/output/function_with_vpc_permission_for_self_managed_kafka.json new file mode 100644 index 000000000..3ecb677fd --- /dev/null +++ b/tests/translator/output/function_with_vpc_permission_for_self_managed_kafka.json @@ -0,0 +1,110 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Parameters": {}, + "Resources": { + "KafkaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.kafka_handler", + "Role": { + "Fn::GetAtt": [ + "KafkaFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionMyKafkaCluster": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "FunctionName": { + "Ref": "KafkaFunction" + }, + "Topics": [ + "Topic1" + ], + "SourceAccessConfigurations": [ + { + "Type": "VPC_SUBNET", + "URI": "subnet:subnet-12345" + }, + { + "Type": "VPC_SECURITY_GROUP", + "URI": "security_group:sg-67890" + } + ], + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": [ + "abc.xyz.com:9092", + "123.45.67.89:9096" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/output/self_managed_kafka_with_intrinsics.json b/tests/translator/output/self_managed_kafka_with_intrinsics.json new file mode 100644 index 000000000..535c0f3d9 --- /dev/null +++ b/tests/translator/output/self_managed_kafka_with_intrinsics.json @@ -0,0 +1,155 @@ +{ + "Parameters": { + "BatchSizeValue": { + "Type": "Number", + "Default": 100 + }, + "EnableValue": { + "Type": "String", + "Default": true + }, + "TopicsValue": { + "Type": "CommaDelimitedList", + "Default": "Topic" + }, + "KafkaBootstrapServersValue": { + "Type": "CommaDelimitedList", + "Default": "abc.xyz.com:9092,123.45.67.89:9096" + } + }, + "Resources": { + "KafkaUserSecret": { + "Type": "AWS::SecretsManager::Secret", + "Properties": { + "Name": "KafkaUserPassword", + "SecretString": { + "Fn::Sub": "{\"username\":\"testBrokerUser\",\"password\":\"testBrokerPassword\"}" + } + } + }, + "KafkaFunction": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "sam-demo-bucket", + "S3Key": "kafka.zip" + }, + "Handler": "index.kafka_handler", + "Role": { + "Fn::GetAtt": [ + "KafkaFunctionRole", + "Arn" + ] + }, + "Runtime": "python3.9", + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "sts:AssumeRole" + ], + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + } + } + ] + }, + "ManagedPolicyArns": [ + "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ], + "Policies": [ + { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "secretsmanager:GetSecretValue" + ], + "Effect": "Allow", + "Resource": { + "Ref": "KafkaUserSecret" + } + }, + { + "Action": [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface", + "ec2:DescribeVpcs", + "ec2:DescribeSubnets", + "ec2:DescribeSecurityGroups" + ], + "Effect": "Allow", + "Resource": "*" + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "SelfManagedKafkaExecutionRolePolicy" + } + ], + "Tags": [ + { + "Key": "lambda:createdBy", + "Value": "SAM" + } + ] + } + }, + "KafkaFunctionMyKafkaCluster": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": { + "Ref": "BatchSizeValue" + }, + "Enabled": { + "Ref": "EnableValue" + }, + "FunctionName": { + "Ref": "KafkaFunction" + }, + "Topics": { + "Ref": "TopicsValue" + }, + "SourceAccessConfigurations": [ + { + "Type": "VPC_SUBNET", + "URI": "subnet:subnet-12345" + }, + { + "Type": "VPC_SECURITY_GROUP", + "URI": "security_group:sg-67890" + }, + { + "Type": "BASIC_AUTH", + "URI": { + "Ref": "KafkaUserSecret" + } + } + ], + "SelfManagedEventSource": { + "Endpoints": { + "KafkaBootstrapServers": { + "Ref": "KafkaBootstrapServersValue" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tests/translator/test_translator.py b/tests/translator/test_translator.py index 4b105fcef..4da050fdc 100644 --- a/tests/translator/test_translator.py +++ b/tests/translator/test_translator.py @@ -450,6 +450,10 @@ class TestTranslatorEndToEnd(AbstractTestTranslator): "api_swagger_integration_with_ref_intrinsic_api_id", "function_with_architectures", "function_with_intrinsic_architecture", + "self_managed_kafka_with_intrinsics", + "function_with_self_managed_kafka", + "function_with_auth_mechanism_for_self_managed_kafka", + "function_with_vpc_permission_for_self_managed_kafka", ], [ ("aws", "ap-southeast-1"),