localstack-core 4.6.1.dev60__py3-none-any.whl → 4.10.1.dev12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- localstack/aws/api/apigateway/__init__.py +87 -110
- localstack/aws/api/cloudformation/__init__.py +18 -4
- localstack/aws/api/cloudwatch/__init__.py +41 -1
- localstack/aws/api/config/__init__.py +4 -0
- localstack/aws/api/core.py +8 -5
- localstack/aws/api/dynamodb/__init__.py +30 -0
- localstack/aws/api/ec2/__init__.py +1545 -66
- localstack/aws/api/events/__init__.py +12 -16
- localstack/aws/api/iam/__init__.py +7 -0
- localstack/aws/api/kinesis/__init__.py +19 -0
- localstack/aws/api/kms/__init__.py +6 -0
- localstack/aws/api/lambda_/__init__.py +36 -23
- localstack/aws/api/logs/__init__.py +20 -8
- localstack/aws/api/opensearch/__init__.py +16 -0
- localstack/aws/api/pipes/__init__.py +24 -32
- localstack/aws/api/redshift/__init__.py +9 -3
- localstack/aws/api/route53/__init__.py +5 -0
- localstack/aws/api/s3/__init__.py +12 -0
- localstack/aws/api/s3control/__init__.py +56 -0
- localstack/aws/api/scheduler/__init__.py +14 -16
- localstack/aws/api/ssm/__init__.py +2 -0
- localstack/aws/api/stepfunctions/__init__.py +88 -114
- localstack/aws/api/support/__init__.py +8 -9
- localstack/aws/api/transcribe/__init__.py +17 -0
- localstack/aws/chain.py +2 -2
- localstack/aws/client.py +14 -11
- localstack/aws/connect.py +42 -41
- localstack/aws/forwarder.py +57 -9
- localstack/aws/gateway.py +1 -3
- localstack/aws/handlers/analytics.py +2 -3
- localstack/aws/handlers/cors.py +4 -5
- localstack/aws/handlers/internal_requests.py +6 -1
- localstack/aws/handlers/logging.py +13 -4
- localstack/aws/handlers/metric_handler.py +44 -5
- localstack/aws/handlers/service.py +48 -28
- localstack/aws/mocking.py +18 -27
- localstack/aws/patches.py +2 -2
- localstack/aws/protocol/op_router.py +11 -10
- localstack/aws/protocol/parser.py +475 -49
- localstack/aws/protocol/serializer.py +723 -106
- localstack/aws/protocol/service_router.py +133 -33
- localstack/aws/protocol/validate.py +6 -6
- localstack/aws/scaffold.py +9 -10
- localstack/aws/serving/edge.py +5 -6
- localstack/aws/serving/hypercorn.py +2 -2
- localstack/aws/serving/twisted.py +1 -2
- localstack/aws/serving/werkzeug.py +2 -2
- localstack/aws/skeleton.py +12 -11
- localstack/aws/spec-patches.json +58 -0
- localstack/aws/spec.py +66 -46
- localstack/cli/exceptions.py +1 -1
- localstack/cli/localstack.py +11 -11
- localstack/cli/lpm.py +4 -5
- localstack/cli/plugins.py +1 -1
- localstack/cli/profiles.py +1 -2
- localstack/config.py +44 -30
- localstack/constants.py +4 -29
- localstack/deprecations.py +5 -5
- localstack/dev/kubernetes/__main__.py +130 -7
- localstack/dev/run/__main__.py +5 -5
- localstack/dev/run/configurators.py +1 -4
- localstack/dev/run/paths.py +6 -6
- localstack/dns/models.py +2 -1
- localstack/dns/plugins.py +5 -1
- localstack/dns/server.py +16 -6
- localstack/http/dispatcher.py +1 -2
- localstack/http/response.py +2 -2
- localstack/http/router.py +1 -1
- localstack/http/trace.py +2 -1
- localstack/logging/format.py +6 -6
- localstack/packages/api.py +13 -12
- localstack/packages/core.py +4 -4
- localstack/packages/debugpy.py +1 -3
- localstack/packages/ffmpeg.py +1 -2
- localstack/packages/java.py +38 -12
- localstack/packages/plugins.py +0 -8
- localstack/runtime/analytics.py +3 -0
- localstack/runtime/hooks.py +1 -1
- localstack/runtime/init.py +8 -9
- localstack/runtime/main.py +5 -5
- localstack/runtime/patches.py +2 -2
- localstack/runtime/shutdown.py +2 -1
- localstack/services/apigateway/exporter.py +1 -2
- localstack/services/apigateway/helpers.py +7 -10
- localstack/services/apigateway/legacy/context.py +21 -21
- localstack/services/apigateway/legacy/helpers.py +27 -28
- localstack/services/apigateway/legacy/integration.py +11 -10
- localstack/services/apigateway/legacy/invocations.py +6 -5
- localstack/services/apigateway/legacy/provider.py +148 -68
- localstack/services/apigateway/legacy/router_asf.py +2 -2
- localstack/services/apigateway/legacy/templates.py +6 -6
- localstack/services/apigateway/models.py +16 -16
- localstack/services/apigateway/next_gen/execute_api/api.py +2 -2
- localstack/services/apigateway/next_gen/execute_api/context.py +25 -25
- localstack/services/apigateway/next_gen/execute_api/handlers/api_key_validation.py +2 -5
- localstack/services/apigateway/next_gen/execute_api/handlers/method_request.py +7 -2
- localstack/services/apigateway/next_gen/execute_api/handlers/parse.py +1 -2
- localstack/services/apigateway/next_gen/execute_api/handlers/resource_router.py +2 -3
- localstack/services/apigateway/next_gen/execute_api/header_utils.py +1 -1
- localstack/services/apigateway/next_gen/execute_api/helpers.py +2 -2
- localstack/services/apigateway/next_gen/execute_api/integrations/aws.py +9 -6
- localstack/services/apigateway/next_gen/execute_api/integrations/http.py +12 -12
- localstack/services/apigateway/next_gen/execute_api/router.py +31 -0
- localstack/services/apigateway/next_gen/execute_api/template_mapping.py +2 -2
- localstack/services/apigateway/next_gen/execute_api/test_invoke.py +114 -9
- localstack/services/apigateway/next_gen/execute_api/variables.py +63 -63
- localstack/services/apigateway/next_gen/provider.py +5 -0
- localstack/services/apigateway/resource_providers/aws_apigateway_account.py +3 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_account_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_apikey.py +14 -14
- localstack/services/apigateway/resource_providers/aws_apigateway_apikey_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_basepathmapping.py +5 -5
- localstack/services/apigateway/resource_providers/aws_apigateway_basepathmapping_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_deployment.py +46 -46
- localstack/services/apigateway/resource_providers/aws_apigateway_deployment_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_domainname.py +18 -18
- localstack/services/apigateway/resource_providers/aws_apigateway_domainname_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_gatewayresponse.py +7 -7
- localstack/services/apigateway/resource_providers/aws_apigateway_gatewayresponse_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_method.py +36 -36
- localstack/services/apigateway/resource_providers/aws_apigateway_method_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_model.py +6 -6
- localstack/services/apigateway/resource_providers/aws_apigateway_model_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_requestvalidator.py +6 -6
- localstack/services/apigateway/resource_providers/aws_apigateway_requestvalidator_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_resource.py +6 -6
- localstack/services/apigateway/resource_providers/aws_apigateway_resource_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_restapi.py +26 -26
- localstack/services/apigateway/resource_providers/aws_apigateway_restapi_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_stage.py +33 -33
- localstack/services/apigateway/resource_providers/aws_apigateway_stage_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_usageplan.py +18 -18
- localstack/services/apigateway/resource_providers/aws_apigateway_usageplan_plugin.py +1 -3
- localstack/services/apigateway/resource_providers/aws_apigateway_usageplankey.py +5 -5
- localstack/services/apigateway/resource_providers/aws_apigateway_usageplankey_plugin.py +1 -3
- localstack/services/cdk/resource_providers/cdk_metadata.py +4 -3
- localstack/services/cdk/resource_providers/cdk_metadata_plugin.py +1 -3
- localstack/services/certificatemanager/resource_providers/aws_certificatemanager_certificate.py +14 -14
- localstack/services/certificatemanager/resource_providers/aws_certificatemanager_certificate_plugin.py +1 -3
- localstack/services/cloudformation/api_utils.py +4 -8
- localstack/services/cloudformation/cfn_utils.py +2 -2
- localstack/services/cloudformation/deployment_utils.py +14 -12
- localstack/services/cloudformation/engine/changes.py +3 -3
- localstack/services/cloudformation/engine/entities.py +27 -17
- localstack/services/cloudformation/engine/parameters.py +4 -4
- localstack/services/cloudformation/engine/template_deployer.py +15 -14
- localstack/services/cloudformation/engine/template_utils.py +34 -12
- localstack/services/cloudformation/engine/transformers.py +11 -8
- localstack/services/cloudformation/engine/types.py +5 -4
- localstack/services/cloudformation/engine/v2/change_set_model.py +336 -39
- localstack/services/cloudformation/engine/v2/change_set_model_describer.py +96 -17
- localstack/services/cloudformation/engine/v2/change_set_model_executor.py +289 -136
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +315 -146
- localstack/services/cloudformation/engine/v2/change_set_model_transform.py +380 -105
- localstack/services/cloudformation/engine/v2/change_set_model_validator.py +183 -0
- localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +6 -0
- localstack/services/cloudformation/engine/v2/resolving.py +102 -0
- localstack/services/cloudformation/engine/yaml_parser.py +9 -2
- localstack/services/cloudformation/provider.py +11 -6
- localstack/services/cloudformation/provider_utils.py +34 -4
- localstack/services/cloudformation/resource_provider.py +31 -21
- localstack/services/cloudformation/resource_providers/aws_cloudformation_macro.py +7 -7
- localstack/services/cloudformation/resource_providers/aws_cloudformation_macro_plugin.py +1 -3
- localstack/services/cloudformation/resource_providers/aws_cloudformation_stack.py +9 -9
- localstack/services/cloudformation/resource_providers/aws_cloudformation_stack_plugin.py +1 -3
- localstack/services/cloudformation/resource_providers/aws_cloudformation_waitcondition.py +6 -6
- localstack/services/cloudformation/resource_providers/aws_cloudformation_waitcondition_plugin.py +1 -3
- localstack/services/cloudformation/resource_providers/aws_cloudformation_waitconditionhandle.py +2 -2
- localstack/services/cloudformation/resource_providers/aws_cloudformation_waitconditionhandle_plugin.py +1 -3
- localstack/services/cloudformation/resources.py +24149 -0
- localstack/services/cloudformation/scaffolding/__main__.py +13 -12
- localstack/services/cloudformation/scaffolding/propgen.py +2 -2
- localstack/services/cloudformation/service_models.py +2 -2
- localstack/services/cloudformation/stores.py +33 -22
- localstack/services/cloudformation/v2/entities.py +103 -88
- localstack/services/cloudformation/v2/provider.py +872 -131
- localstack/services/cloudformation/v2/types.py +38 -0
- localstack/services/cloudformation/v2/utils.py +4 -1
- localstack/services/cloudwatch/alarm_scheduler.py +11 -8
- localstack/services/cloudwatch/cloudwatch_database_helper.py +4 -5
- localstack/services/cloudwatch/models.py +5 -7
- localstack/services/cloudwatch/provider.py +30 -25
- localstack/services/cloudwatch/provider_v2.py +27 -32
- localstack/services/cloudwatch/resource_providers/aws_cloudwatch_alarm.py +40 -40
- localstack/services/cloudwatch/resource_providers/aws_cloudwatch_alarm_plugin.py +1 -3
- localstack/services/cloudwatch/resource_providers/aws_cloudwatch_compositealarm.py +12 -12
- localstack/services/cloudwatch/resource_providers/aws_cloudwatch_compositealarm_plugin.py +1 -3
- localstack/services/dynamodb/packages.py +3 -3
- localstack/services/dynamodb/provider.py +159 -18
- localstack/services/dynamodb/resource_providers/aws_dynamodb_globaltable.py +63 -63
- localstack/services/dynamodb/resource_providers/aws_dynamodb_globaltable_plugin.py +1 -3
- localstack/services/dynamodb/resource_providers/aws_dynamodb_table.py +57 -57
- localstack/services/dynamodb/resource_providers/aws_dynamodb_table_plugin.py +1 -3
- localstack/services/dynamodb/server.py +2 -2
- localstack/services/dynamodb/utils.py +13 -14
- localstack/services/dynamodb/v2/provider.py +157 -18
- localstack/services/dynamodbstreams/dynamodbstreams_api.py +2 -2
- localstack/services/dynamodbstreams/models.py +1 -3
- localstack/services/ec2/patches.py +26 -5
- localstack/services/ec2/provider.py +3 -3
- localstack/services/ec2/resource_providers/aws_ec2_dhcpoptions.py +10 -10
- localstack/services/ec2/resource_providers/aws_ec2_dhcpoptions_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_instance.py +96 -96
- localstack/services/ec2/resource_providers/aws_ec2_instance_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_internetgateway.py +5 -5
- localstack/services/ec2/resource_providers/aws_ec2_internetgateway_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_keypair.py +10 -10
- localstack/services/ec2/resource_providers/aws_ec2_keypair_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_natgateway.py +13 -13
- localstack/services/ec2/resource_providers/aws_ec2_natgateway_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_networkacl.py +6 -6
- localstack/services/ec2/resource_providers/aws_ec2_networkacl_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_prefixlist.py +14 -14
- localstack/services/ec2/resource_providers/aws_ec2_prefixlist_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_route.py +15 -15
- localstack/services/ec2/resource_providers/aws_ec2_route_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_routetable.py +6 -6
- localstack/services/ec2/resource_providers/aws_ec2_routetable_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_securitygroup.py +29 -29
- localstack/services/ec2/resource_providers/aws_ec2_securitygroup_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_subnet.py +19 -19
- localstack/services/ec2/resource_providers/aws_ec2_subnet_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_subnetroutetableassociation.py +4 -4
- localstack/services/ec2/resource_providers/aws_ec2_subnetroutetableassociation_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_transitgateway.py +16 -16
- localstack/services/ec2/resource_providers/aws_ec2_transitgateway_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_transitgatewayattachment.py +9 -9
- localstack/services/ec2/resource_providers/aws_ec2_transitgatewayattachment_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_vpc.py +15 -15
- localstack/services/ec2/resource_providers/aws_ec2_vpc_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_vpcendpoint.py +29 -22
- localstack/services/ec2/resource_providers/aws_ec2_vpcendpoint_plugin.py +1 -3
- localstack/services/ec2/resource_providers/aws_ec2_vpcgatewayattachment.py +5 -5
- localstack/services/ec2/resource_providers/aws_ec2_vpcgatewayattachment_plugin.py +1 -3
- localstack/services/ecr/resource_providers/aws_ecr_repository.py +22 -19
- localstack/services/ecr/resource_providers/aws_ecr_repository_plugin.py +1 -3
- localstack/services/edge.py +6 -6
- localstack/services/es/provider.py +21 -21
- localstack/services/events/archive.py +2 -2
- localstack/services/events/connection.py +5 -5
- localstack/services/events/event_bus.py +9 -9
- localstack/services/events/event_rule_engine.py +31 -13
- localstack/services/events/models.py +29 -30
- localstack/services/events/provider.py +29 -26
- localstack/services/events/replay.py +3 -3
- localstack/services/events/resource_providers/aws_events_apidestination.py +8 -8
- localstack/services/events/resource_providers/aws_events_apidestination_plugin.py +1 -3
- localstack/services/events/resource_providers/aws_events_connection.py +27 -27
- localstack/services/events/resource_providers/aws_events_connection_plugin.py +1 -3
- localstack/services/events/resource_providers/aws_events_eventbus.py +9 -9
- localstack/services/events/resource_providers/aws_events_eventbus_plugin.py +1 -3
- localstack/services/events/resource_providers/aws_events_eventbuspolicy.py +11 -11
- localstack/services/events/resource_providers/aws_events_eventbuspolicy_plugin.py +1 -3
- localstack/services/events/resource_providers/aws_events_rule.py +82 -82
- localstack/services/events/resource_providers/aws_events_rule_plugin.py +1 -3
- localstack/services/events/rule.py +15 -15
- localstack/services/events/target.py +21 -13
- localstack/services/events/utils.py +7 -7
- localstack/services/events/v1/models.py +1 -3
- localstack/services/events/v1/provider.py +15 -15
- localstack/services/firehose/models.py +1 -3
- localstack/services/firehose/provider.py +25 -16
- localstack/services/iam/iam_patches.py +5 -6
- localstack/services/iam/provider.py +14 -119
- localstack/services/iam/resource_providers/aws_iam_accesskey.py +6 -6
- localstack/services/iam/resource_providers/aws_iam_accesskey_plugin.py +1 -3
- localstack/services/iam/resource_providers/aws_iam_group.py +9 -9
- localstack/services/iam/resource_providers/aws_iam_group_plugin.py +1 -3
- localstack/services/iam/resource_providers/aws_iam_instanceprofile.py +5 -5
- localstack/services/iam/resource_providers/aws_iam_instanceprofile_plugin.py +1 -3
- localstack/services/iam/resource_providers/aws_iam_managedpolicy.py +9 -9
- localstack/services/iam/resource_providers/aws_iam_managedpolicy_plugin.py +1 -3
- localstack/services/iam/resource_providers/aws_iam_policy.py +7 -7
- localstack/services/iam/resource_providers/aws_iam_policy_plugin.py +1 -3
- localstack/services/iam/resource_providers/aws_iam_role.py +16 -16
- localstack/services/iam/resource_providers/aws_iam_role_plugin.py +1 -3
- localstack/services/iam/resource_providers/aws_iam_servercertificate.py +10 -10
- localstack/services/iam/resource_providers/aws_iam_servercertificate_plugin.py +1 -3
- localstack/services/iam/resource_providers/aws_iam_servicelinkedrole.py +5 -5
- localstack/services/iam/resource_providers/aws_iam_servicelinkedrole_plugin.py +1 -3
- localstack/services/iam/resource_providers/aws_iam_user.py +17 -17
- localstack/services/iam/resource_providers/aws_iam_user_plugin.py +1 -3
- localstack/services/iam/resources/policy_simulator.py +133 -0
- localstack/services/kinesis/kinesis_mock_server.py +7 -8
- localstack/services/kinesis/models.py +17 -5
- localstack/services/kinesis/packages.py +3 -3
- localstack/services/kinesis/provider.py +86 -3
- localstack/services/kinesis/resource_providers/aws_kinesis_stream.py +13 -13
- localstack/services/kinesis/resource_providers/aws_kinesis_stream_plugin.py +1 -3
- localstack/services/kinesis/resource_providers/aws_kinesis_streamconsumer.py +7 -7
- localstack/services/kinesis/resource_providers/aws_kinesis_streamconsumer_plugin.py +1 -3
- localstack/services/kinesisfirehose/resource_providers/aws_kinesisfirehose_deliverystream.py +191 -191
- localstack/services/kinesisfirehose/resource_providers/aws_kinesisfirehose_deliverystream_plugin.py +1 -3
- localstack/services/kms/models.py +21 -18
- localstack/services/kms/provider.py +62 -26
- localstack/services/kms/resource_providers/aws_kms_alias.py +3 -3
- localstack/services/kms/resource_providers/aws_kms_alias_plugin.py +1 -3
- localstack/services/kms/resource_providers/aws_kms_key.py +14 -14
- localstack/services/kms/resource_providers/aws_kms_key_plugin.py +1 -3
- localstack/services/kms/utils.py +4 -3
- localstack/services/lambda_/api_utils.py +19 -16
- localstack/services/lambda_/custom_endpoints.py +2 -2
- localstack/services/lambda_/event_source_mapping/esm_worker_factory.py +4 -7
- localstack/services/lambda_/event_source_mapping/pipe_utils.py +2 -2
- localstack/services/lambda_/event_source_mapping/pollers/stream_poller.py +1 -1
- localstack/services/lambda_/event_source_mapping/senders/sender_utils.py +2 -1
- localstack/services/lambda_/hooks.py +6 -1
- localstack/services/lambda_/invocation/assignment.py +1 -2
- localstack/services/lambda_/invocation/docker_runtime_executor.py +7 -11
- localstack/services/lambda_/invocation/event_manager.py +1 -1
- localstack/services/lambda_/invocation/execution_environment.py +4 -4
- localstack/services/lambda_/invocation/executor_endpoint.py +8 -11
- localstack/services/lambda_/invocation/internal_sqs_queue.py +6 -10
- localstack/services/lambda_/invocation/lambda_models.py +33 -30
- localstack/services/lambda_/invocation/lambda_service.py +12 -5
- localstack/services/lambda_/invocation/logs.py +2 -3
- localstack/services/lambda_/invocation/runtime_executor.py +3 -3
- localstack/services/lambda_/invocation/version_manager.py +31 -8
- localstack/services/lambda_/ldm.py +14 -0
- localstack/services/lambda_/packages.py +3 -4
- localstack/services/lambda_/provider.py +9 -26
- localstack/services/lambda_/provider_utils.py +1 -1
- localstack/services/lambda_/resource_providers/aws_lambda_codesigningconfig.py +8 -8
- localstack/services/lambda_/resource_providers/aws_lambda_codesigningconfig_plugin.py +1 -3
- localstack/services/lambda_/resource_providers/aws_lambda_eventinvokeconfig.py +11 -11
- localstack/services/lambda_/resource_providers/aws_lambda_eventinvokeconfig_plugin.py +1 -3
- localstack/services/lambda_/resource_providers/aws_lambda_eventsourcemapping.py +39 -39
- localstack/services/lambda_/resource_providers/aws_lambda_eventsourcemapping_plugin.py +1 -3
- localstack/services/lambda_/resource_providers/aws_lambda_function.py +54 -54
- localstack/services/lambda_/resource_providers/aws_lambda_function_plugin.py +1 -3
- localstack/services/lambda_/resource_providers/aws_lambda_layerversion.py +11 -11
- localstack/services/lambda_/resource_providers/aws_lambda_layerversion_plugin.py +1 -3
- localstack/services/lambda_/resource_providers/aws_lambda_layerversionpermission.py +6 -6
- localstack/services/lambda_/resource_providers/aws_lambda_layerversionpermission_plugin.py +1 -3
- localstack/services/lambda_/resource_providers/aws_lambda_permission.py +10 -10
- localstack/services/lambda_/resource_providers/aws_lambda_permission_plugin.py +1 -3
- localstack/services/lambda_/resource_providers/aws_lambda_url.py +15 -15
- localstack/services/lambda_/resource_providers/aws_lambda_url_plugin.py +1 -3
- localstack/services/lambda_/resource_providers/aws_lambda_version.py +8 -8
- localstack/services/lambda_/resource_providers/aws_lambda_version_plugin.py +1 -3
- localstack/services/lambda_/resource_providers/lambda_alias.py +12 -12
- localstack/services/lambda_/resource_providers/lambda_alias_plugin.py +1 -3
- localstack/services/lambda_/runtimes.py +1 -3
- localstack/services/lambda_/urlrouter.py +14 -1
- localstack/services/logs/models.py +1 -3
- localstack/services/logs/provider.py +39 -22
- localstack/services/logs/resource_providers/aws_logs_loggroup.py +9 -9
- localstack/services/logs/resource_providers/aws_logs_loggroup_plugin.py +1 -3
- localstack/services/logs/resource_providers/aws_logs_logstream.py +4 -4
- localstack/services/logs/resource_providers/aws_logs_logstream_plugin.py +1 -3
- localstack/services/logs/resource_providers/aws_logs_subscriptionfilter.py +7 -7
- localstack/services/logs/resource_providers/aws_logs_subscriptionfilter_plugin.py +1 -3
- localstack/services/moto.py +5 -4
- localstack/services/opensearch/cluster.py +28 -20
- localstack/services/opensearch/cluster_manager.py +13 -14
- localstack/services/opensearch/models.py +1 -3
- localstack/services/opensearch/packages.py +27 -9
- localstack/services/opensearch/provider.py +15 -10
- localstack/services/opensearch/resource_providers/aws_elasticsearch_domain.py +61 -61
- localstack/services/opensearch/resource_providers/aws_elasticsearch_domain_plugin.py +1 -3
- localstack/services/opensearch/resource_providers/aws_opensearchservice_domain.py +89 -89
- localstack/services/opensearch/resource_providers/aws_opensearchservice_domain_plugin.py +1 -3
- localstack/services/opensearch/versions.py +57 -10
- localstack/services/plugins.py +37 -32
- localstack/services/providers.py +10 -2
- localstack/services/redshift/provider.py +0 -21
- localstack/services/redshift/resource_providers/aws_redshift_cluster.py +57 -57
- localstack/services/redshift/resource_providers/aws_redshift_cluster_plugin.py +1 -3
- localstack/services/resource_groups/resource_providers/aws_resourcegroups_group.py +21 -21
- localstack/services/resource_groups/resource_providers/aws_resourcegroups_group_plugin.py +1 -3
- localstack/services/route53/models.py +1 -3
- localstack/services/route53/provider.py +1 -2
- localstack/services/route53/resource_providers/aws_route53_healthcheck.py +6 -6
- localstack/services/route53/resource_providers/aws_route53_healthcheck_plugin.py +1 -3
- localstack/services/route53/resource_providers/aws_route53_recordset.py +27 -27
- localstack/services/route53/resource_providers/aws_route53_recordset_plugin.py +1 -3
- localstack/services/route53resolver/models.py +8 -10
- localstack/services/route53resolver/provider.py +12 -12
- localstack/services/s3/codec.py +2 -2
- localstack/services/s3/constants.py +5 -2
- localstack/services/s3/cors.py +8 -8
- localstack/services/s3/models.py +73 -73
- localstack/services/s3/notifications.py +74 -58
- localstack/services/s3/presigned_url.py +41 -59
- localstack/services/s3/provider.py +86 -29
- localstack/services/s3/resource_providers/aws_s3_bucket.py +180 -180
- localstack/services/s3/resource_providers/aws_s3_bucket_plugin.py +1 -3
- localstack/services/s3/resource_providers/aws_s3_bucketpolicy.py +4 -4
- localstack/services/s3/resource_providers/aws_s3_bucketpolicy_plugin.py +1 -3
- localstack/services/s3/storage/core.py +4 -3
- localstack/services/s3/storage/ephemeral.py +7 -6
- localstack/services/s3/utils.py +51 -31
- localstack/services/s3/validation.py +47 -33
- localstack/services/s3/website_hosting.py +9 -7
- localstack/services/scheduler/resource_providers/aws_scheduler_schedule.py +60 -60
- localstack/services/scheduler/resource_providers/aws_scheduler_schedule_plugin.py +1 -3
- localstack/services/scheduler/resource_providers/aws_scheduler_schedulegroup.py +9 -9
- localstack/services/scheduler/resource_providers/aws_scheduler_schedulegroup_plugin.py +1 -3
- localstack/services/secretsmanager/provider.py +10 -12
- localstack/services/secretsmanager/resource_providers/aws_secretsmanager_resourcepolicy.py +5 -5
- localstack/services/secretsmanager/resource_providers/aws_secretsmanager_resourcepolicy_plugin.py +1 -3
- localstack/services/secretsmanager/resource_providers/aws_secretsmanager_rotationschedule.py +21 -21
- localstack/services/secretsmanager/resource_providers/aws_secretsmanager_rotationschedule_plugin.py +1 -3
- localstack/services/secretsmanager/resource_providers/aws_secretsmanager_secret.py +23 -23
- localstack/services/secretsmanager/resource_providers/aws_secretsmanager_secret_plugin.py +1 -3
- localstack/services/secretsmanager/resource_providers/aws_secretsmanager_secrettargetattachment.py +5 -5
- localstack/services/secretsmanager/resource_providers/aws_secretsmanager_secrettargetattachment_plugin.py +1 -3
- localstack/services/ses/models.py +8 -1
- localstack/services/ses/provider.py +128 -55
- localstack/services/ses/resource_providers/aws_ses_emailidentity.py +21 -21
- localstack/services/ses/resource_providers/aws_ses_emailidentity_plugin.py +1 -3
- localstack/services/sns/constants.py +7 -1
- localstack/services/sns/executor.py +9 -2
- localstack/services/sns/models.py +25 -25
- localstack/services/sns/provider.py +9 -7
- localstack/services/sns/publisher.py +37 -23
- localstack/services/sns/resource_providers/aws_sns_subscription.py +13 -13
- localstack/services/sns/resource_providers/aws_sns_subscription_plugin.py +1 -3
- localstack/services/sns/resource_providers/aws_sns_topic.py +16 -16
- localstack/services/sns/resource_providers/aws_sns_topic_plugin.py +1 -3
- localstack/services/sns/resource_providers/aws_sns_topicpolicy.py +4 -4
- localstack/services/sns/resource_providers/aws_sns_topicpolicy_plugin.py +1 -3
- localstack/services/sns/v2/models.py +167 -0
- localstack/services/sns/v2/provider.py +867 -0
- localstack/services/sns/v2/utils.py +130 -0
- localstack/services/sqs/constants.py +2 -2
- localstack/services/sqs/developer_api.py +205 -0
- localstack/services/sqs/models.py +71 -36
- localstack/services/sqs/provider.py +56 -332
- localstack/services/sqs/query_api.py +8 -5
- localstack/services/sqs/resource_providers/aws_sqs_queue.py +21 -21
- localstack/services/sqs/resource_providers/aws_sqs_queue_plugin.py +1 -3
- localstack/services/sqs/resource_providers/aws_sqs_queuepolicy.py +7 -5
- localstack/services/sqs/resource_providers/aws_sqs_queuepolicy_plugin.py +1 -3
- localstack/services/sqs/utils.py +123 -4
- localstack/services/ssm/provider.py +3 -4
- localstack/services/ssm/resource_providers/aws_ssm_maintenancewindow.py +15 -15
- localstack/services/ssm/resource_providers/aws_ssm_maintenancewindow_plugin.py +1 -3
- localstack/services/ssm/resource_providers/aws_ssm_maintenancewindowtarget.py +10 -10
- localstack/services/ssm/resource_providers/aws_ssm_maintenancewindowtarget_plugin.py +1 -3
- localstack/services/ssm/resource_providers/aws_ssm_maintenancewindowtask.py +48 -48
- localstack/services/ssm/resource_providers/aws_ssm_maintenancewindowtask_plugin.py +1 -3
- localstack/services/ssm/resource_providers/aws_ssm_parameter.py +10 -10
- localstack/services/ssm/resource_providers/aws_ssm_parameter_plugin.py +1 -3
- localstack/services/ssm/resource_providers/aws_ssm_patchbaseline.py +29 -29
- localstack/services/ssm/resource_providers/aws_ssm_patchbaseline_plugin.py +1 -3
- localstack/services/stepfunctions/asl/antlt4utils/antlr4utils.py +3 -4
- localstack/services/stepfunctions/asl/component/common/assign/assign_decl.py +1 -1
- localstack/services/stepfunctions/asl/component/common/assign/assign_decl_binding.py +1 -1
- localstack/services/stepfunctions/asl/component/common/assign/assign_template_value_array.py +1 -1
- localstack/services/stepfunctions/asl/component/common/assign/assign_template_value_object.py +1 -1
- localstack/services/stepfunctions/asl/component/common/catch/catcher_decl.py +9 -9
- localstack/services/stepfunctions/asl/component/common/error_name/custom_error_name.py +2 -2
- localstack/services/stepfunctions/asl/component/common/error_name/error_name.py +4 -4
- localstack/services/stepfunctions/asl/component/common/error_name/failure_event.py +8 -8
- localstack/services/stepfunctions/asl/component/common/error_name/states_error_name_type.py +1 -1
- localstack/services/stepfunctions/asl/component/common/jsonata/jsonata_template_binding.py +2 -2
- localstack/services/stepfunctions/asl/component/common/jsonata/jsonata_template_value_array.py +1 -1
- localstack/services/stepfunctions/asl/component/common/jsonata/jsonata_template_value_object.py +1 -1
- localstack/services/stepfunctions/asl/component/common/path/input_path.py +4 -4
- localstack/services/stepfunctions/asl/component/common/path/output_path.py +4 -4
- localstack/services/stepfunctions/asl/component/common/path/result_path.py +3 -3
- localstack/services/stepfunctions/asl/component/common/payload/payloadvalue/payloadarr/payload_arr.py +1 -1
- localstack/services/stepfunctions/asl/component/common/payload/payloadvalue/payloadbinding/payload_binding.py +3 -3
- localstack/services/stepfunctions/asl/component/common/payload/payloadvalue/payloadtmpl/payload_tmpl.py +1 -1
- localstack/services/stepfunctions/asl/component/common/retry/retrier_decl.py +8 -8
- localstack/services/stepfunctions/asl/component/common/string/string_expression.py +3 -3
- localstack/services/stepfunctions/asl/component/common/timeouts/timeout.py +3 -3
- localstack/services/stepfunctions/asl/component/eval_component.py +2 -3
- localstack/services/stepfunctions/asl/component/intrinsic/argument/argument.py +4 -4
- localstack/services/stepfunctions/asl/component/intrinsic/function/statesfunction/array/array_partition.py +1 -1
- localstack/services/stepfunctions/asl/component/intrinsic/function/statesfunction/generic/string_format.py +1 -1
- localstack/services/stepfunctions/asl/component/intrinsic/function/statesfunction/states_function_array.py +1 -1
- localstack/services/stepfunctions/asl/component/intrinsic/function/statesfunction/states_function_format.py +1 -1
- localstack/services/stepfunctions/asl/component/intrinsic/jsonata.py +3 -3
- localstack/services/stepfunctions/asl/component/intrinsic/member.py +1 -1
- localstack/services/stepfunctions/asl/component/program/program.py +9 -9
- localstack/services/stepfunctions/asl/component/program/states.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state.py +10 -11
- localstack/services/stepfunctions/asl/component/state/state_choice/choice_rule.py +11 -11
- localstack/services/stepfunctions/asl/component/state/state_choice/comparison/comparison.py +5 -11
- localstack/services/stepfunctions/asl/component/state/state_choice/comparison/operator/implementations/is_operator.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_choice/state_choice.py +3 -5
- localstack/services/stepfunctions/asl/component/state/state_execution/execute_state.py +9 -9
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/item_reader/item_reader_decl.py +7 -7
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/item_reader/reader_config/reader_config_decl.py +5 -5
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/item_reader/resource_eval/resource_eval_s3.py +2 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/item_reader/resource_eval/resource_output_transformer/resource_output_transformer_csv.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/distributed_iteration_component.py +8 -8
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/inline_iteration_component.py +7 -7
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/itemprocessor/distributed_item_processor_worker.py +4 -4
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/itemprocessor/inline_item_processor_worker.py +5 -5
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/itemprocessor/map_run_record.py +10 -10
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/iteration_component.py +3 -3
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/iteration_declaration.py +3 -3
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/iteration_worker.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/iterator/distributed_iterator_worker.py +3 -5
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/iterator/inline_iterator.py +1 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/iterator/inline_iterator_worker.py +5 -5
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/iteration/job.py +9 -9
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/result_writer/resource_eval/resource_eval_s3.py +2 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +12 -13
- localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/branch_worker.py +3 -3
- localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/branches_decl.py +7 -7
- localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/state_parallel.py +2 -3
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/lambda_eval_utils.py +7 -7
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/resource.py +3 -3
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service.py +10 -10
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_api_gateway.py +18 -18
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_aws_sdk.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_batch.py +8 -7
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_callback.py +18 -17
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_dynamodb.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_ecs.py +5 -4
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_events.py +4 -4
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_glue.py +7 -6
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_lambda.py +4 -4
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_sfn.py +9 -8
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_sns.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/service/state_task_service_sqs.py +4 -4
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/state_task.py +5 -5
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/state_task_lambda.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +3 -5
- localstack/services/stepfunctions/asl/component/state/state_pass/state_pass.py +5 -7
- localstack/services/stepfunctions/asl/component/state/state_succeed/state_succeed.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_wait/state_wait.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_wait/wait_function/timestamp.py +2 -2
- localstack/services/stepfunctions/asl/eval/callback/callback.py +18 -18
- localstack/services/stepfunctions/asl/eval/environment.py +22 -24
- localstack/services/stepfunctions/asl/eval/evaluation_details.py +3 -5
- localstack/services/stepfunctions/asl/eval/event/event_manager.py +10 -10
- localstack/services/stepfunctions/asl/eval/event/logging.py +3 -3
- localstack/services/stepfunctions/asl/eval/program_state.py +8 -8
- localstack/services/stepfunctions/asl/eval/states.py +12 -12
- localstack/services/stepfunctions/asl/eval/test_state/environment.py +3 -5
- localstack/services/stepfunctions/asl/eval/variable_store.py +6 -6
- localstack/services/stepfunctions/asl/jsonata/jsonata.py +7 -6
- localstack/services/stepfunctions/asl/parse/asl_parser.py +1 -1
- localstack/services/stepfunctions/asl/parse/intrinsic/preprocessor.py +2 -3
- localstack/services/stepfunctions/asl/parse/preprocessor.py +44 -44
- localstack/services/stepfunctions/asl/parse/typed_props.py +2 -2
- localstack/services/stepfunctions/asl/static_analyser/intrinsic/variable_names_intrinsic_static_analyser.py +1 -1
- localstack/services/stepfunctions/asl/static_analyser/variable_references_static_analyser.py +2 -2
- localstack/services/stepfunctions/asl/utils/encoding.py +2 -2
- localstack/services/stepfunctions/asl/utils/json_path.py +2 -2
- localstack/services/stepfunctions/backend/activity.py +4 -4
- localstack/services/stepfunctions/backend/alias.py +8 -8
- localstack/services/stepfunctions/backend/execution.py +29 -30
- localstack/services/stepfunctions/backend/execution_worker.py +7 -7
- localstack/services/stepfunctions/backend/state_machine.py +28 -28
- localstack/services/stepfunctions/backend/test_state/execution.py +3 -4
- localstack/services/stepfunctions/backend/test_state/execution_worker.py +1 -3
- localstack/services/stepfunctions/mocking/mock_config.py +9 -9
- localstack/services/stepfunctions/mocking/mock_config_file.py +10 -10
- localstack/services/stepfunctions/packages.py +14 -5
- localstack/services/stepfunctions/provider.py +34 -44
- localstack/services/stepfunctions/quotas.py +2 -3
- localstack/services/stepfunctions/resource_providers/aws_stepfunctions_activity.py +6 -6
- localstack/services/stepfunctions/resource_providers/aws_stepfunctions_activity_plugin.py +1 -3
- localstack/services/stepfunctions/resource_providers/aws_stepfunctions_statemachine.py +25 -25
- localstack/services/stepfunctions/resource_providers/aws_stepfunctions_statemachine_plugin.py +1 -3
- localstack/services/stepfunctions/stepfunctions_utils.py +1 -2
- localstack/services/stores.py +8 -8
- localstack/services/transcribe/packages.py +1 -3
- localstack/services/transcribe/provider.py +8 -3
- localstack/state/codecs.py +61 -0
- localstack/state/core.py +11 -5
- localstack/state/inspect.py +4 -4
- localstack/state/pickle.py +36 -23
- localstack/testing/aws/asf_utils.py +3 -2
- localstack/testing/aws/cloudformation_utils.py +1 -1
- localstack/testing/aws/lambda_utils.py +15 -14
- localstack/testing/aws/util.py +3 -2
- localstack/testing/pytest/cloudformation/fixtures.py +68 -18
- localstack/testing/pytest/container.py +5 -5
- localstack/testing/pytest/filters.py +1 -3
- localstack/testing/pytest/fixtures.py +188 -49
- localstack/testing/pytest/in_memory_localstack.py +1 -3
- localstack/testing/pytest/marking.py +42 -15
- localstack/testing/pytest/path_filter.py +1 -1
- localstack/testing/pytest/stepfunctions/fixtures.py +4 -4
- localstack/testing/pytest/stepfunctions/utils.py +11 -10
- localstack/testing/pytest/util.py +1 -1
- localstack/testing/pytest/validation_tracking.py +3 -4
- localstack/testing/scenario/provisioning.py +11 -10
- localstack/testing/snapshots/transformer_utility.py +8 -3
- localstack/testing/testselection/matching.py +2 -2
- localstack/testing/testselection/opt_out.py +1 -1
- localstack/testing/testselection/scripts/filter_by_test_selection.py +1 -1
- localstack/testing/testselection/scripts/generate_test_selection.py +1 -1
- localstack/testing/testselection/testselection.py +2 -2
- localstack/utils/analytics/cli.py +2 -3
- localstack/utils/analytics/client.py +5 -5
- localstack/utils/analytics/events.py +2 -2
- localstack/utils/analytics/metadata.py +6 -4
- localstack/utils/analytics/metrics/counter.py +11 -18
- localstack/utils/analytics/metrics/registry.py +2 -2
- localstack/utils/analytics/publisher.py +4 -5
- localstack/utils/analytics/service_providers.py +19 -0
- localstack/utils/analytics/service_request_aggregator.py +4 -4
- localstack/utils/archives.py +12 -12
- localstack/utils/asyncio.py +2 -2
- localstack/utils/aws/arns.py +26 -31
- localstack/utils/aws/aws_responses.py +21 -28
- localstack/utils/aws/aws_stack.py +7 -12
- localstack/utils/aws/dead_letter_queue.py +4 -9
- localstack/utils/aws/message_forwarding.py +8 -11
- localstack/utils/aws/request_context.py +5 -6
- localstack/utils/aws/resources.py +1 -1
- localstack/utils/aws/templating.py +4 -4
- localstack/utils/batch_policy.py +4 -4
- localstack/utils/bootstrap.py +37 -30
- localstack/utils/catalog/catalog.py +139 -0
- localstack/utils/catalog/catalog_loader.py +119 -0
- localstack/utils/catalog/common.py +58 -0
- localstack/utils/catalog/plugins.py +28 -0
- localstack/utils/cloudwatch/cloudwatch_util.py +5 -5
- localstack/utils/collections.py +33 -27
- localstack/utils/config_listener.py +2 -2
- localstack/utils/container_networking.py +5 -6
- localstack/utils/container_utils/container_client.py +156 -160
- localstack/utils/container_utils/docker_cmd_client.py +97 -81
- localstack/utils/container_utils/docker_sdk_client.py +75 -72
- localstack/utils/crypto.py +12 -13
- localstack/utils/diagnose.py +11 -12
- localstack/utils/docker_utils.py +11 -7
- localstack/utils/files.py +34 -15
- localstack/utils/functions.py +5 -4
- localstack/utils/http.py +14 -14
- localstack/utils/iputils.py +2 -1
- localstack/utils/json.py +21 -7
- localstack/utils/kinesis/kinesis_connector.py +2 -1
- localstack/utils/net.py +25 -17
- localstack/utils/no_exit_argument_parser.py +2 -2
- localstack/utils/numbers.py +9 -2
- localstack/utils/objects.py +15 -14
- localstack/utils/patch.py +14 -7
- localstack/utils/platform.py +2 -2
- localstack/utils/run.py +15 -14
- localstack/utils/scheduler.py +13 -12
- localstack/utils/server/tcp_proxy.py +2 -2
- localstack/utils/serving.py +3 -4
- localstack/utils/strings.py +15 -16
- localstack/utils/sync.py +126 -1
- localstack/utils/tagging.py +10 -8
- localstack/utils/testutil.py +17 -17
- localstack/utils/threads.py +3 -3
- localstack/utils/time.py +12 -4
- localstack/utils/urls.py +1 -3
- localstack/utils/xml.py +1 -1
- localstack/utils/xray/traceid.py +1 -1
- localstack/version.py +16 -3
- {localstack_core-4.6.1.dev60.dist-info → localstack_core-4.10.1.dev12.dist-info}/METADATA +18 -13
- {localstack_core-4.6.1.dev60.dist-info → localstack_core-4.10.1.dev12.dist-info}/RECORD +663 -655
- {localstack_core-4.6.1.dev60.dist-info → localstack_core-4.10.1.dev12.dist-info}/entry_points.txt +8 -4
- localstack_core-4.10.1.dev12.dist-info/plux.json +1 -0
- localstack/packages/terraform.py +0 -47
- localstack/services/cloudformation/deploy.html +0 -144
- localstack/services/cloudformation/deploy_ui.py +0 -47
- localstack/services/cloudformation/plugins.py +0 -12
- localstack/services/lambda_/lambda_debug_mode/ldm.py +0 -375
- localstack/services/lambda_/lambda_debug_mode/ldm_config_file.py +0 -178
- localstack/services/lambda_/lambda_debug_mode/ldm_types.py +0 -11
- localstack/services/lambda_/lambda_debug_mode/ldm_utils.py +0 -43
- localstack_core-4.6.1.dev60.dist-info/plux.json +0 -1
- /localstack/{services/lambda_/lambda_debug_mode/__init__.py → testing/pytest/cloudformation/transformers.py} +0 -0
- {localstack_core-4.6.1.dev60.data → localstack_core-4.10.1.dev12.data}/scripts/localstack +0 -0
- {localstack_core-4.6.1.dev60.data → localstack_core-4.10.1.dev12.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.6.1.dev60.data → localstack_core-4.10.1.dev12.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.6.1.dev60.dist-info → localstack_core-4.10.1.dev12.dist-info}/WHEEL +0 -0
- {localstack_core-4.6.1.dev60.dist-info → localstack_core-4.10.1.dev12.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.6.1.dev60.dist-info → localstack_core-4.10.1.dev12.dist-info}/top_level.txt +0 -0
|
@@ -3,18 +3,15 @@ from __future__ import annotations
|
|
|
3
3
|
import base64
|
|
4
4
|
import copy
|
|
5
5
|
import re
|
|
6
|
-
from
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from typing import Any, Final, Generic, TypeVar
|
|
7
8
|
|
|
8
9
|
from botocore.exceptions import ClientError
|
|
9
10
|
|
|
10
11
|
from localstack import config
|
|
12
|
+
from localstack.aws.api.cloudformation import ResourceStatus
|
|
11
13
|
from localstack.aws.api.ec2 import AvailabilityZoneList, DescribeAvailabilityZonesResult
|
|
12
14
|
from localstack.aws.connect import connect_to
|
|
13
|
-
from localstack.services.cloudformation.engine.transformers import (
|
|
14
|
-
Transformer,
|
|
15
|
-
execute_macro,
|
|
16
|
-
transformers,
|
|
17
|
-
)
|
|
18
15
|
from localstack.services.cloudformation.engine.v2.change_set_model import (
|
|
19
16
|
ChangeSetEntity,
|
|
20
17
|
ChangeType,
|
|
@@ -33,6 +30,7 @@ from localstack.services.cloudformation.engine.v2.change_set_model import (
|
|
|
33
30
|
NodeProperties,
|
|
34
31
|
NodeProperty,
|
|
35
32
|
NodeResource,
|
|
33
|
+
NodeResources,
|
|
36
34
|
NodeTemplate,
|
|
37
35
|
Nothing,
|
|
38
36
|
NothingType,
|
|
@@ -47,9 +45,19 @@ from localstack.services.cloudformation.engine.v2.change_set_model import (
|
|
|
47
45
|
from localstack.services.cloudformation.engine.v2.change_set_model_visitor import (
|
|
48
46
|
ChangeSetModelVisitor,
|
|
49
47
|
)
|
|
50
|
-
from localstack.services.cloudformation.
|
|
48
|
+
from localstack.services.cloudformation.engine.v2.resolving import (
|
|
49
|
+
REGEX_DYNAMIC_REF,
|
|
50
|
+
extract_dynamic_reference,
|
|
51
|
+
perform_dynamic_reference_lookup,
|
|
52
|
+
)
|
|
53
|
+
from localstack.services.cloudformation.engine.validations import ValidationError
|
|
54
|
+
from localstack.services.cloudformation.stores import (
|
|
55
|
+
exports_map,
|
|
56
|
+
)
|
|
51
57
|
from localstack.services.cloudformation.v2.entities import ChangeSet
|
|
58
|
+
from localstack.services.cloudformation.v2.types import ResolvedResource
|
|
52
59
|
from localstack.utils.aws.arns import get_partition
|
|
60
|
+
from localstack.utils.numbers import to_number
|
|
53
61
|
from localstack.utils.objects import get_value_from_path
|
|
54
62
|
from localstack.utils.run import to_str
|
|
55
63
|
from localstack.utils.strings import to_bytes
|
|
@@ -70,9 +78,15 @@ _PSEUDO_PARAMETERS: Final[set[str]] = {
|
|
|
70
78
|
|
|
71
79
|
TBefore = TypeVar("TBefore")
|
|
72
80
|
TAfter = TypeVar("TAfter")
|
|
81
|
+
_T = TypeVar("_T")
|
|
73
82
|
|
|
83
|
+
REGEX_OUTPUT_APIGATEWAY = re.compile(
|
|
84
|
+
rf"^(https?://.+\.execute-api\.)(?:[^-]+-){{2,3}}\d\.(amazonaws\.com|{_AWS_URL_SUFFIX})/?(.*)$"
|
|
85
|
+
)
|
|
74
86
|
MOCKED_REFERENCE = "unknown"
|
|
75
87
|
|
|
88
|
+
VALID_LOGICAL_RESOURCE_ID_RE = re.compile(r"^[A-Za-z0-9]+$")
|
|
89
|
+
|
|
76
90
|
|
|
77
91
|
class PreprocEntityDelta(Generic[TBefore, TAfter]):
|
|
78
92
|
before: Maybe[TBefore]
|
|
@@ -102,20 +116,24 @@ class PreprocProperties:
|
|
|
102
116
|
|
|
103
117
|
class PreprocResource:
|
|
104
118
|
logical_id: str
|
|
105
|
-
physical_resource_id:
|
|
106
|
-
condition:
|
|
119
|
+
physical_resource_id: str | None
|
|
120
|
+
condition: bool | None
|
|
107
121
|
resource_type: str
|
|
108
122
|
properties: PreprocProperties
|
|
109
|
-
depends_on:
|
|
123
|
+
depends_on: list[str] | None
|
|
124
|
+
requires_replacement: bool
|
|
125
|
+
status: ResourceStatus | None
|
|
110
126
|
|
|
111
127
|
def __init__(
|
|
112
128
|
self,
|
|
113
129
|
logical_id: str,
|
|
114
130
|
physical_resource_id: str,
|
|
115
|
-
condition:
|
|
131
|
+
condition: bool | None,
|
|
116
132
|
resource_type: str,
|
|
117
133
|
properties: PreprocProperties,
|
|
118
|
-
depends_on:
|
|
134
|
+
depends_on: list[str] | None,
|
|
135
|
+
requires_replacement: bool,
|
|
136
|
+
status: ResourceStatus | None = None,
|
|
119
137
|
):
|
|
120
138
|
self.logical_id = logical_id
|
|
121
139
|
self.physical_resource_id = physical_resource_id
|
|
@@ -123,6 +141,8 @@ class PreprocResource:
|
|
|
123
141
|
self.resource_type = resource_type
|
|
124
142
|
self.properties = properties
|
|
125
143
|
self.depends_on = depends_on
|
|
144
|
+
self.requires_replacement = requires_replacement
|
|
145
|
+
self.status = status
|
|
126
146
|
|
|
127
147
|
@staticmethod
|
|
128
148
|
def _compare_conditions(c1: bool, c2: bool):
|
|
@@ -147,10 +167,10 @@ class PreprocResource:
|
|
|
147
167
|
class PreprocOutput:
|
|
148
168
|
name: str
|
|
149
169
|
value: Any
|
|
150
|
-
export:
|
|
151
|
-
condition:
|
|
170
|
+
export: Any | None
|
|
171
|
+
condition: bool | None
|
|
152
172
|
|
|
153
|
-
def __init__(self, name: str, value: Any, export:
|
|
173
|
+
def __init__(self, name: str, value: Any, export: Any | None, condition: bool | None):
|
|
154
174
|
self.name = name
|
|
155
175
|
self.value = value
|
|
156
176
|
self.export = export
|
|
@@ -178,8 +198,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
178
198
|
def __init__(self, change_set: ChangeSet):
|
|
179
199
|
self._change_set = change_set
|
|
180
200
|
self._before_resolved_resources = change_set.stack.resolved_resources
|
|
181
|
-
self._before_cache =
|
|
182
|
-
self._after_cache =
|
|
201
|
+
self._before_cache = {}
|
|
202
|
+
self._after_cache = {}
|
|
183
203
|
|
|
184
204
|
def _setup_runtime_cache(self) -> None:
|
|
185
205
|
runtime_cache_key = self.__class__.__name__
|
|
@@ -207,6 +227,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
207
227
|
def process(self) -> None:
|
|
208
228
|
self._setup_runtime_cache()
|
|
209
229
|
node_template = self._change_set.update_model.node_template
|
|
230
|
+
node_conditions = self._change_set.update_model.node_template.conditions
|
|
231
|
+
self.visit(node_conditions)
|
|
210
232
|
self.visit(node_template)
|
|
211
233
|
self._save_runtime_cache()
|
|
212
234
|
|
|
@@ -218,11 +240,13 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
218
240
|
if node_resource.name == resource_name:
|
|
219
241
|
self.visit(node_resource)
|
|
220
242
|
return node_resource
|
|
221
|
-
raise
|
|
243
|
+
raise ValidationError(
|
|
244
|
+
f"Template format error: Unresolved resource dependencies [{resource_name}] in the Resources block of the template"
|
|
245
|
+
)
|
|
222
246
|
|
|
223
247
|
def _get_node_property_for(
|
|
224
248
|
self, property_name: str, node_resource: NodeResource
|
|
225
|
-
) ->
|
|
249
|
+
) -> NodeProperty | None:
|
|
226
250
|
# TODO: this could be improved with hashmap lookups if the Node contained bindings and not lists.
|
|
227
251
|
for node_property in node_resource.properties.properties:
|
|
228
252
|
if node_property.name == property_name:
|
|
@@ -233,7 +257,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
233
257
|
def _deployed_property_value_of(
|
|
234
258
|
self, resource_logical_id: str, property_name: str, resolved_resources: dict
|
|
235
259
|
) -> Any:
|
|
236
|
-
#
|
|
260
|
+
# We have to override this function to make sure it does not try to access the
|
|
261
|
+
# resolved resource
|
|
237
262
|
|
|
238
263
|
# Before we can obtain deployed value for a resource, we need to first ensure to
|
|
239
264
|
# process the resource if this wasn't processed already. Ideally, values should only
|
|
@@ -248,17 +273,17 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
248
273
|
raise RuntimeError(
|
|
249
274
|
f"No deployed instances of resource '{resource_logical_id}' were found"
|
|
250
275
|
)
|
|
251
|
-
properties = resolved_resource.get("Properties",
|
|
252
|
-
# support structured properties, e.g. NestedStack.Outputs.OutputName
|
|
253
|
-
property_value:
|
|
276
|
+
properties = resolved_resource.get("Properties", {})
|
|
277
|
+
# TODO support structured properties, e.g. NestedStack.Outputs.OutputName
|
|
278
|
+
property_value: Any | None = get_value_from_path(properties, property_name)
|
|
254
279
|
|
|
255
280
|
if property_value:
|
|
256
|
-
if not isinstance(property_value, str):
|
|
257
|
-
#
|
|
258
|
-
#
|
|
259
|
-
#
|
|
281
|
+
if not isinstance(property_value, (str, list, dict)):
|
|
282
|
+
# Str: Standard expected type. TODO validate bools and numbers
|
|
283
|
+
# List: Multiple resource types can return a list of values e.g. AWS::EC2::VPC.
|
|
284
|
+
# Dict: Custom resources in CloudFormation can return arbitrary data structures.
|
|
260
285
|
raise RuntimeError(
|
|
261
|
-
f"Accessing property '{property_name}' from '{resource_logical_id}' resulted in a non-string value"
|
|
286
|
+
f"Accessing property '{property_name}' from '{resource_logical_id}' resulted in a non-string value nor list"
|
|
262
287
|
)
|
|
263
288
|
return property_value
|
|
264
289
|
elif config.CFN_IGNORE_UNSUPPORTED_RESOURCE_TYPES:
|
|
@@ -277,7 +302,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
277
302
|
|
|
278
303
|
def _after_deployed_property_value_of(
|
|
279
304
|
self, resource_logical_id: str, property_name: str
|
|
280
|
-
) ->
|
|
305
|
+
) -> str | None:
|
|
281
306
|
return self._before_deployed_property_value_of(
|
|
282
307
|
resource_logical_id=resource_logical_id, property_name=property_name
|
|
283
308
|
)
|
|
@@ -367,8 +392,12 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
367
392
|
node_mapping: NodeMapping = self._get_node_mapping(map_name=map_name)
|
|
368
393
|
top_level_value = node_mapping.bindings.bindings.get(top_level_key)
|
|
369
394
|
if not isinstance(top_level_value, NodeObject):
|
|
370
|
-
|
|
395
|
+
error_key = "::".join([map_name, top_level_key, second_level_key])
|
|
396
|
+
raise ValidationError(f"Template error: Unable to get mapping for {error_key}")
|
|
371
397
|
second_level_value = top_level_value.bindings.get(second_level_key)
|
|
398
|
+
if not isinstance(second_level_value, (TerminalValue, NodeArray, NodeObject)):
|
|
399
|
+
error_key = "::".join([map_name, top_level_key, second_level_key])
|
|
400
|
+
raise ValidationError(f"Template error: Unable to get mapping for {error_key}")
|
|
372
401
|
mapping_value_delta = self.visit(second_level_value)
|
|
373
402
|
return mapping_value_delta
|
|
374
403
|
|
|
@@ -380,10 +409,63 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
380
409
|
return PreprocEntityDelta(before=before, after=after)
|
|
381
410
|
delta = super().visit(change_set_entity=change_set_entity)
|
|
382
411
|
if isinstance(delta, PreprocEntityDelta):
|
|
412
|
+
delta = self._maybe_perform_replacements(delta)
|
|
383
413
|
self._before_cache[entity_scope] = delta.before
|
|
384
414
|
self._after_cache[entity_scope] = delta.after
|
|
385
415
|
return delta
|
|
386
416
|
|
|
417
|
+
def _maybe_perform_replacements(self, delta: PreprocEntityDelta) -> PreprocEntityDelta:
|
|
418
|
+
delta = self._maybe_perform_static_replacements(delta)
|
|
419
|
+
delta = self._maybe_perform_dynamic_replacements(delta)
|
|
420
|
+
return delta
|
|
421
|
+
|
|
422
|
+
def _maybe_perform_static_replacements(self, delta: PreprocEntityDelta) -> PreprocEntityDelta:
|
|
423
|
+
return self._maybe_perform_on_delta(delta, self._perform_static_replacements)
|
|
424
|
+
|
|
425
|
+
def _maybe_perform_dynamic_replacements(self, delta: PreprocEntityDelta) -> PreprocEntityDelta:
|
|
426
|
+
return self._maybe_perform_on_delta(delta, self._perform_dynamic_replacements)
|
|
427
|
+
|
|
428
|
+
def _maybe_perform_on_delta(
|
|
429
|
+
self, delta: PreprocEntityDelta | None, f: Callable[[_T], _T]
|
|
430
|
+
) -> PreprocEntityDelta | None:
|
|
431
|
+
if isinstance(delta.before, str):
|
|
432
|
+
delta.before = f(delta.before)
|
|
433
|
+
if isinstance(delta.after, str):
|
|
434
|
+
delta.after = f(delta.after)
|
|
435
|
+
return delta
|
|
436
|
+
|
|
437
|
+
def _perform_dynamic_replacements(self, value: _T) -> _T:
|
|
438
|
+
if not isinstance(value, str):
|
|
439
|
+
return value
|
|
440
|
+
|
|
441
|
+
if dynamic_ref := extract_dynamic_reference(value):
|
|
442
|
+
new_value = perform_dynamic_reference_lookup(
|
|
443
|
+
reference=dynamic_ref,
|
|
444
|
+
account_id=self._change_set.account_id,
|
|
445
|
+
region_name=self._change_set.region_name,
|
|
446
|
+
)
|
|
447
|
+
if new_value:
|
|
448
|
+
# We need to use a function here, to avoid backslash processing by regex.
|
|
449
|
+
# From the regex sub documentation:
|
|
450
|
+
# repl can be a string or a function; if it is a string, any backslash escapes in it are processed.
|
|
451
|
+
# Using a function, we can avoid this processing.
|
|
452
|
+
return REGEX_DYNAMIC_REF.sub(lambda _: new_value, value)
|
|
453
|
+
|
|
454
|
+
return value
|
|
455
|
+
|
|
456
|
+
@staticmethod
|
|
457
|
+
def _perform_static_replacements(value: str) -> str:
|
|
458
|
+
api_match = REGEX_OUTPUT_APIGATEWAY.match(value)
|
|
459
|
+
if api_match and value not in config.CFN_STRING_REPLACEMENT_DENY_LIST:
|
|
460
|
+
prefix = api_match[1]
|
|
461
|
+
host = api_match[2]
|
|
462
|
+
path = api_match[3]
|
|
463
|
+
port = localstack_host().port
|
|
464
|
+
value = f"{prefix}{host}:{port}/{path}"
|
|
465
|
+
return value
|
|
466
|
+
|
|
467
|
+
return value
|
|
468
|
+
|
|
387
469
|
def _cached_apply(
|
|
388
470
|
self, scope: Scope, arguments_delta: PreprocEntityDelta, resolver: Callable[[Any], Any]
|
|
389
471
|
) -> PreprocEntityDelta:
|
|
@@ -432,6 +514,9 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
432
514
|
|
|
433
515
|
return PreprocEntityDelta(before=before, after=after)
|
|
434
516
|
|
|
517
|
+
def visit_node_property(self, node_property: NodeProperty) -> PreprocEntityDelta:
|
|
518
|
+
return self.visit(node_property.value)
|
|
519
|
+
|
|
435
520
|
def visit_terminal_value_modified(
|
|
436
521
|
self, terminal_value_modified: TerminalValueModified
|
|
437
522
|
) -> PreprocEntityDelta:
|
|
@@ -465,15 +550,15 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
465
550
|
|
|
466
551
|
def visit_node_object(self, node_object: NodeObject) -> PreprocEntityDelta:
|
|
467
552
|
node_change_type = node_object.change_type
|
|
468
|
-
before =
|
|
469
|
-
after =
|
|
553
|
+
before = {} if node_change_type != ChangeType.CREATED else Nothing
|
|
554
|
+
after = {} if node_change_type != ChangeType.REMOVED else Nothing
|
|
470
555
|
for name, change_set_entity in node_object.bindings.items():
|
|
471
556
|
delta: PreprocEntityDelta = self.visit(change_set_entity=change_set_entity)
|
|
472
557
|
delta_before = delta.before
|
|
473
558
|
delta_after = delta.after
|
|
474
|
-
if not is_nothing(before) and not is_nothing(delta_before)
|
|
559
|
+
if not is_nothing(before) and not is_nothing(delta_before):
|
|
475
560
|
before[name] = delta_before
|
|
476
|
-
if not is_nothing(after) and not is_nothing(delta_after)
|
|
561
|
+
if not is_nothing(after) and not is_nothing(delta_after):
|
|
477
562
|
after[name] = delta_after
|
|
478
563
|
return PreprocEntityDelta(before=before, after=after)
|
|
479
564
|
|
|
@@ -484,33 +569,57 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
484
569
|
arguments_list = arguments.split(".")
|
|
485
570
|
else:
|
|
486
571
|
arguments_list = arguments
|
|
572
|
+
|
|
573
|
+
if len(arguments_list) < 2:
|
|
574
|
+
raise ValidationError(
|
|
575
|
+
"Template error: every Fn::GetAtt object requires two non-empty parameters, the resource name and the resource attribute"
|
|
576
|
+
)
|
|
577
|
+
|
|
487
578
|
logical_name_of_resource = arguments_list[0]
|
|
488
|
-
attribute_name = arguments_list[1]
|
|
579
|
+
attribute_name = ".".join(arguments_list[1:])
|
|
489
580
|
|
|
490
581
|
node_resource = self._get_node_resource_for(
|
|
491
582
|
resource_name=logical_name_of_resource,
|
|
492
583
|
node_template=self._change_set.update_model.node_template,
|
|
493
584
|
)
|
|
494
|
-
|
|
585
|
+
|
|
586
|
+
if not is_nothing(node_resource.condition_reference):
|
|
587
|
+
condition = self._get_node_condition_if_exists(node_resource.condition_reference.value)
|
|
588
|
+
evaluation_result = self._resolve_condition(condition.name)
|
|
589
|
+
|
|
590
|
+
if select_before and not evaluation_result.before:
|
|
591
|
+
raise ValidationError(
|
|
592
|
+
f"Template format error: Unresolved resource dependencies [{logical_name_of_resource}] in the Resources block of the template"
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
if not select_before and not evaluation_result.after:
|
|
596
|
+
raise ValidationError(
|
|
597
|
+
f"Template format error: Unresolved resource dependencies [{logical_name_of_resource}] in the Resources block of the template"
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
# Custom Resources can mutate their definition
|
|
601
|
+
# So the preproc should search first in the resource values and then check the template
|
|
602
|
+
if select_before:
|
|
603
|
+
value = self._before_deployed_property_value_of(
|
|
604
|
+
resource_logical_id=logical_name_of_resource,
|
|
605
|
+
property_name=attribute_name,
|
|
606
|
+
)
|
|
607
|
+
else:
|
|
608
|
+
value = self._after_deployed_property_value_of(
|
|
609
|
+
resource_logical_id=logical_name_of_resource,
|
|
610
|
+
property_name=attribute_name,
|
|
611
|
+
)
|
|
612
|
+
if value is not None:
|
|
613
|
+
return value
|
|
614
|
+
|
|
615
|
+
node_property: NodeProperty | None = self._get_node_property_for(
|
|
495
616
|
property_name=attribute_name, node_resource=node_resource
|
|
496
617
|
)
|
|
497
618
|
if node_property is not None:
|
|
498
619
|
# The property is statically defined in the template and its value can be computed.
|
|
499
620
|
property_delta = self.visit(node_property)
|
|
500
621
|
value = property_delta.before if select_before else property_delta.after
|
|
501
|
-
|
|
502
|
-
# The property is not statically defined and must therefore be available in
|
|
503
|
-
# the properties deployed set.
|
|
504
|
-
if select_before:
|
|
505
|
-
value = self._before_deployed_property_value_of(
|
|
506
|
-
resource_logical_id=logical_name_of_resource,
|
|
507
|
-
property_name=attribute_name,
|
|
508
|
-
)
|
|
509
|
-
else:
|
|
510
|
-
value = self._after_deployed_property_value_of(
|
|
511
|
-
resource_logical_id=logical_name_of_resource,
|
|
512
|
-
property_name=attribute_name,
|
|
513
|
-
)
|
|
622
|
+
|
|
514
623
|
return value
|
|
515
624
|
|
|
516
625
|
def visit_node_intrinsic_function_fn_get_att(
|
|
@@ -539,6 +648,12 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
539
648
|
return args[0] == args[1]
|
|
540
649
|
|
|
541
650
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
|
651
|
+
|
|
652
|
+
if isinstance(arguments_delta.after, list) and len(arguments_delta.after) != 2:
|
|
653
|
+
raise ValidationError(
|
|
654
|
+
"Template error: every Fn::Equals object requires a list of 2 string parameters."
|
|
655
|
+
)
|
|
656
|
+
|
|
542
657
|
delta = self._cached_apply(
|
|
543
658
|
scope=node_intrinsic_function.scope,
|
|
544
659
|
arguments_delta=arguments_delta,
|
|
@@ -549,21 +664,38 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
549
664
|
def visit_node_intrinsic_function_fn_if(
|
|
550
665
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
|
551
666
|
) -> PreprocEntityDelta:
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
after=args[1] if boolean_expression_delta.after else args[2],
|
|
667
|
+
# `if` needs to be short-circuiting i.e. if the condition is True we don't evaluate the
|
|
668
|
+
# False branch. If the condition is False, we don't evaluate the True branch.
|
|
669
|
+
if len(node_intrinsic_function.arguments.array) != 3:
|
|
670
|
+
raise ValueError(
|
|
671
|
+
f"Incorrectly constructed Fn::If usage, expected 3 arguments, found {len(node_intrinsic_function.arguments.array)}"
|
|
558
672
|
)
|
|
559
673
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
674
|
+
condition_delta = self.visit(node_intrinsic_function.arguments.array[0])
|
|
675
|
+
if_delta = PreprocEntityDelta()
|
|
676
|
+
if not is_nothing(condition_delta.before):
|
|
677
|
+
node_condition = self._get_node_condition_if_exists(
|
|
678
|
+
condition_name=condition_delta.before
|
|
679
|
+
)
|
|
680
|
+
condition_value = self.visit(node_condition).before
|
|
681
|
+
if condition_value:
|
|
682
|
+
arg_delta = self.visit(node_intrinsic_function.arguments.array[1])
|
|
683
|
+
else:
|
|
684
|
+
arg_delta = self.visit(node_intrinsic_function.arguments.array[2])
|
|
685
|
+
if_delta.before = arg_delta.before
|
|
686
|
+
|
|
687
|
+
if not is_nothing(condition_delta.after):
|
|
688
|
+
node_condition = self._get_node_condition_if_exists(
|
|
689
|
+
condition_name=condition_delta.after
|
|
690
|
+
)
|
|
691
|
+
condition_value = self.visit(node_condition).after
|
|
692
|
+
if condition_value:
|
|
693
|
+
arg_delta = self.visit(node_intrinsic_function.arguments.array[1])
|
|
694
|
+
else:
|
|
695
|
+
arg_delta = self.visit(node_intrinsic_function.arguments.array[2])
|
|
696
|
+
if_delta.after = arg_delta.after
|
|
697
|
+
|
|
698
|
+
return if_delta
|
|
567
699
|
|
|
568
700
|
def visit_node_intrinsic_function_fn_and(
|
|
569
701
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
|
@@ -598,8 +730,12 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
598
730
|
def visit_node_intrinsic_function_fn_not(
|
|
599
731
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
|
600
732
|
) -> PreprocEntityDelta:
|
|
601
|
-
def _compute_fn_not(arg: bool) -> bool:
|
|
602
|
-
|
|
733
|
+
def _compute_fn_not(arg: list[bool] | bool) -> bool:
|
|
734
|
+
# Is the argument ever a lone boolean?
|
|
735
|
+
if isinstance(arg, list):
|
|
736
|
+
return not arg[0]
|
|
737
|
+
else:
|
|
738
|
+
return not arg
|
|
603
739
|
|
|
604
740
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
|
605
741
|
delta = self._cached_apply(
|
|
@@ -609,65 +745,6 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
609
745
|
)
|
|
610
746
|
return delta
|
|
611
747
|
|
|
612
|
-
def _compute_fn_transform(self, args: dict[str, Any]) -> Any:
|
|
613
|
-
# TODO: add typing to arguments before this level.
|
|
614
|
-
# TODO: add schema validation
|
|
615
|
-
# TODO: add support for other transform types
|
|
616
|
-
|
|
617
|
-
account_id = self._change_set.account_id
|
|
618
|
-
region_name = self._change_set.region_name
|
|
619
|
-
transform_name: str = args.get("Name")
|
|
620
|
-
if not isinstance(transform_name, str):
|
|
621
|
-
raise RuntimeError("Invalid or missing Fn::Transform 'Name' argument")
|
|
622
|
-
transform_parameters: dict = args.get("Parameters")
|
|
623
|
-
if not isinstance(transform_parameters, dict):
|
|
624
|
-
raise RuntimeError("Invalid or missing Fn::Transform 'Parameters' argument")
|
|
625
|
-
|
|
626
|
-
if transform_name in transformers:
|
|
627
|
-
# TODO: port and refactor this 'transformers' logic to this package.
|
|
628
|
-
builtin_transformer_class = transformers[transform_name]
|
|
629
|
-
builtin_transformer: Transformer = builtin_transformer_class()
|
|
630
|
-
transform_output: Any = builtin_transformer.transform(
|
|
631
|
-
account_id=account_id, region_name=region_name, parameters=transform_parameters
|
|
632
|
-
)
|
|
633
|
-
return transform_output
|
|
634
|
-
|
|
635
|
-
macros_store = get_cloudformation_store(
|
|
636
|
-
account_id=account_id, region_name=region_name
|
|
637
|
-
).macros
|
|
638
|
-
if transform_name in macros_store:
|
|
639
|
-
# TODO: this formatting of stack parameters is odd but required to integrate with v1 execute_macro util.
|
|
640
|
-
# consider porting this utils and passing the plain list of parameters instead.
|
|
641
|
-
stack_parameters = {
|
|
642
|
-
parameter["ParameterKey"]: parameter
|
|
643
|
-
for parameter in self._change_set.stack.parameters
|
|
644
|
-
}
|
|
645
|
-
transform_output: Any = execute_macro(
|
|
646
|
-
account_id=account_id,
|
|
647
|
-
region_name=region_name,
|
|
648
|
-
parsed_template=dict(), # TODO: review the requirements for this argument.
|
|
649
|
-
macro=args, # TODO: review support for non dict bindings (v1).
|
|
650
|
-
stack_parameters=stack_parameters,
|
|
651
|
-
transformation_parameters=transform_parameters,
|
|
652
|
-
is_intrinsic=True,
|
|
653
|
-
)
|
|
654
|
-
return transform_output
|
|
655
|
-
|
|
656
|
-
raise RuntimeError(
|
|
657
|
-
f"Unsupported transform function '{transform_name}' in '{self._change_set.stack.stack_name}'"
|
|
658
|
-
)
|
|
659
|
-
|
|
660
|
-
def visit_node_intrinsic_function_fn_transform(
|
|
661
|
-
self, node_intrinsic_function: NodeIntrinsicFunction
|
|
662
|
-
) -> PreprocEntityDelta:
|
|
663
|
-
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
|
664
|
-
delta = self._cached_apply(
|
|
665
|
-
scope=node_intrinsic_function.scope,
|
|
666
|
-
arguments_delta=arguments_delta,
|
|
667
|
-
resolver=self._compute_fn_transform,
|
|
668
|
-
)
|
|
669
|
-
return delta
|
|
670
|
-
|
|
671
748
|
def visit_node_intrinsic_function_fn_sub(
|
|
672
749
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
|
673
750
|
) -> PreprocEntityDelta:
|
|
@@ -677,7 +754,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
677
754
|
sub_parameters: dict
|
|
678
755
|
if isinstance(args, str):
|
|
679
756
|
string_template = args
|
|
680
|
-
sub_parameters =
|
|
757
|
+
sub_parameters = {}
|
|
681
758
|
elif (
|
|
682
759
|
isinstance(args, list)
|
|
683
760
|
and len(args) == 2
|
|
@@ -784,7 +861,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
784
861
|
if values == "":
|
|
785
862
|
return ""
|
|
786
863
|
raise RuntimeError(f"Invalid arguments list definition for Fn::Join: '{args}'")
|
|
787
|
-
str_values: list[str] =
|
|
864
|
+
str_values: list[str] = []
|
|
788
865
|
for value in values:
|
|
789
866
|
if value is None:
|
|
790
867
|
continue
|
|
@@ -806,13 +883,27 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
806
883
|
):
|
|
807
884
|
# TODO: add further support for schema validation
|
|
808
885
|
def _compute_fn_select(args: list[Any]) -> Any:
|
|
809
|
-
values
|
|
886
|
+
values = args[1]
|
|
887
|
+
# defer evaluation if the selection list contains unresolved elements (e.g., unresolved intrinsics)
|
|
888
|
+
if isinstance(values, list) and not all(isinstance(value, str) for value in values):
|
|
889
|
+
raise RuntimeError("Fn::Select list contains unresolved elements")
|
|
890
|
+
|
|
810
891
|
if not isinstance(values, list) or not values:
|
|
811
|
-
raise
|
|
892
|
+
raise ValidationError(
|
|
893
|
+
"Template error: Fn::Select requires a list argument with two elements: an integer index and a list"
|
|
894
|
+
)
|
|
895
|
+
try:
|
|
896
|
+
index: int = int(args[0])
|
|
897
|
+
except ValueError as e:
|
|
898
|
+
raise ValidationError(
|
|
899
|
+
"Template error: Fn::Select requires a list argument with two elements: an integer index and a list"
|
|
900
|
+
) from e
|
|
901
|
+
|
|
812
902
|
values_len = len(values)
|
|
813
|
-
index
|
|
814
|
-
|
|
815
|
-
|
|
903
|
+
if index < 0 or index >= values_len:
|
|
904
|
+
raise ValidationError(
|
|
905
|
+
"Template error: Fn::Select requires a list argument with two elements: an integer index and a list"
|
|
906
|
+
)
|
|
816
907
|
selection = values[index]
|
|
817
908
|
return selection
|
|
818
909
|
|
|
@@ -839,6 +930,17 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
839
930
|
return split_string
|
|
840
931
|
|
|
841
932
|
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
|
933
|
+
|
|
934
|
+
if not (
|
|
935
|
+
is_nothing(arguments_delta.after)
|
|
936
|
+
or isinstance(arguments_delta.after, list)
|
|
937
|
+
and len(arguments_delta.after) == 2
|
|
938
|
+
):
|
|
939
|
+
raise ValidationError(
|
|
940
|
+
"Template error: every Fn::Split object requires two parameters, "
|
|
941
|
+
"(1) a string delimiter and (2) a string to be split or a function that returns a string to be split."
|
|
942
|
+
)
|
|
943
|
+
|
|
842
944
|
delta = self._cached_apply(
|
|
843
945
|
scope=node_intrinsic_function.scope,
|
|
844
946
|
arguments_delta=arguments_delta,
|
|
@@ -925,8 +1027,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
925
1027
|
def visit_node_parameters(
|
|
926
1028
|
self, node_parameters: NodeParameters
|
|
927
1029
|
) -> PreprocEntityDelta[dict[str, Any], dict[str, Any]]:
|
|
928
|
-
before_parameters =
|
|
929
|
-
after_parameters =
|
|
1030
|
+
before_parameters = {}
|
|
1031
|
+
after_parameters = {}
|
|
930
1032
|
for parameter in node_parameters.parameters:
|
|
931
1033
|
parameter_delta = self.visit(parameter)
|
|
932
1034
|
parameter_before = parameter_delta.before
|
|
@@ -938,6 +1040,10 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
938
1040
|
return PreprocEntityDelta(before=before_parameters, after=after_parameters)
|
|
939
1041
|
|
|
940
1042
|
def visit_node_parameter(self, node_parameter: NodeParameter) -> PreprocEntityDelta:
|
|
1043
|
+
if not VALID_LOGICAL_RESOURCE_ID_RE.match(node_parameter.name):
|
|
1044
|
+
raise ValidationError(
|
|
1045
|
+
f"Template format error: Parameter name {node_parameter.name} is non alphanumeric."
|
|
1046
|
+
)
|
|
941
1047
|
dynamic_value = node_parameter.dynamic_value
|
|
942
1048
|
dynamic_delta = self.visit(dynamic_value)
|
|
943
1049
|
|
|
@@ -951,8 +1057,11 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
951
1057
|
|
|
952
1058
|
def _resolve_parameter_type(value: str, type_: str) -> Any:
|
|
953
1059
|
match type_:
|
|
954
|
-
case "List<String>":
|
|
1060
|
+
case "List<String>" | "CommaDelimitedList":
|
|
955
1061
|
return [item.strip() for item in value.split(",")]
|
|
1062
|
+
case "Number":
|
|
1063
|
+
# TODO: validate the parameter type at template parse time (or whatever is in parity with AWS) so we know this cannot fail
|
|
1064
|
+
return to_number(value)
|
|
956
1065
|
return value
|
|
957
1066
|
|
|
958
1067
|
if not is_nothing(after):
|
|
@@ -969,11 +1078,17 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
969
1078
|
return delta
|
|
970
1079
|
|
|
971
1080
|
def _resource_physical_resource_id_from(
|
|
972
|
-
self, logical_resource_id: str, resolved_resources: dict
|
|
973
|
-
) -> str:
|
|
1081
|
+
self, logical_resource_id: str, resolved_resources: dict[str, ResolvedResource]
|
|
1082
|
+
) -> str | None:
|
|
974
1083
|
# TODO: typing around resolved resources is needed and should be reflected here.
|
|
975
|
-
resolved_resource = resolved_resources.get(logical_resource_id,
|
|
976
|
-
|
|
1084
|
+
resolved_resource = resolved_resources.get(logical_resource_id, {})
|
|
1085
|
+
if resolved_resource.get("ResourceStatus") not in {
|
|
1086
|
+
ResourceStatus.CREATE_COMPLETE,
|
|
1087
|
+
ResourceStatus.UPDATE_COMPLETE,
|
|
1088
|
+
}:
|
|
1089
|
+
return None
|
|
1090
|
+
|
|
1091
|
+
physical_resource_id = resolved_resource.get("PhysicalResourceId")
|
|
977
1092
|
if not isinstance(physical_resource_id, str):
|
|
978
1093
|
raise RuntimeError(f"No PhysicalResourceId found for resource '{logical_resource_id}'")
|
|
979
1094
|
return physical_resource_id
|
|
@@ -992,6 +1107,9 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
992
1107
|
self, node_intrinsic_function: NodeIntrinsicFunction
|
|
993
1108
|
) -> PreprocEntityDelta:
|
|
994
1109
|
def _compute_fn_ref(logical_id: str) -> PreprocEntityDelta:
|
|
1110
|
+
if logical_id == "AWS::NoValue":
|
|
1111
|
+
return Nothing
|
|
1112
|
+
|
|
995
1113
|
reference_delta: PreprocEntityDelta = self._resolve_reference(logical_id=logical_id)
|
|
996
1114
|
if isinstance(before := reference_delta.before, PreprocResource):
|
|
997
1115
|
reference_delta.before = before.physical_resource_id
|
|
@@ -1028,8 +1146,8 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
1028
1146
|
|
|
1029
1147
|
def visit_node_array(self, node_array: NodeArray) -> PreprocEntityDelta:
|
|
1030
1148
|
node_change_type = node_array.change_type
|
|
1031
|
-
before =
|
|
1032
|
-
after =
|
|
1149
|
+
before = [] if node_change_type != ChangeType.CREATED else Nothing
|
|
1150
|
+
after = [] if node_change_type != ChangeType.REMOVED else Nothing
|
|
1033
1151
|
for change_set_entity in node_array.array:
|
|
1034
1152
|
delta: PreprocEntityDelta = self.visit(change_set_entity=change_set_entity)
|
|
1035
1153
|
delta_before = delta.before
|
|
@@ -1040,15 +1158,12 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
1040
1158
|
after.append(delta_after)
|
|
1041
1159
|
return PreprocEntityDelta(before=before, after=after)
|
|
1042
1160
|
|
|
1043
|
-
def visit_node_property(self, node_property: NodeProperty) -> PreprocEntityDelta:
|
|
1044
|
-
return self.visit(node_property.value)
|
|
1045
|
-
|
|
1046
1161
|
def visit_node_properties(
|
|
1047
1162
|
self, node_properties: NodeProperties
|
|
1048
1163
|
) -> PreprocEntityDelta[PreprocProperties, PreprocProperties]:
|
|
1049
1164
|
node_change_type = node_properties.change_type
|
|
1050
|
-
before_bindings =
|
|
1051
|
-
after_bindings =
|
|
1165
|
+
before_bindings = {} if node_change_type != ChangeType.CREATED else Nothing
|
|
1166
|
+
after_bindings = {} if node_change_type != ChangeType.REMOVED else Nothing
|
|
1052
1167
|
for node_property in node_properties.properties:
|
|
1053
1168
|
property_name = node_property.name
|
|
1054
1169
|
delta = self.visit(node_property)
|
|
@@ -1088,9 +1203,27 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
1088
1203
|
after = after_delta.after
|
|
1089
1204
|
return PreprocEntityDelta(before=before, after=after)
|
|
1090
1205
|
|
|
1206
|
+
def visit_node_resources(self, node_resources: NodeResources):
|
|
1207
|
+
"""
|
|
1208
|
+
Skip resources where they conditionally evaluate to False
|
|
1209
|
+
"""
|
|
1210
|
+
for node_resource in node_resources.resources:
|
|
1211
|
+
if not is_nothing(node_resource.condition_reference):
|
|
1212
|
+
condition_delta = self._resolve_resource_condition_reference(
|
|
1213
|
+
node_resource.condition_reference
|
|
1214
|
+
)
|
|
1215
|
+
condition_after = condition_delta.after
|
|
1216
|
+
if condition_after is False:
|
|
1217
|
+
continue
|
|
1218
|
+
self.visit(node_resource)
|
|
1219
|
+
|
|
1091
1220
|
def visit_node_resource(
|
|
1092
1221
|
self, node_resource: NodeResource
|
|
1093
1222
|
) -> PreprocEntityDelta[PreprocResource, PreprocResource]:
|
|
1223
|
+
if not VALID_LOGICAL_RESOURCE_ID_RE.match(node_resource.name):
|
|
1224
|
+
raise ValidationError(
|
|
1225
|
+
f"Template format error: Resource name {node_resource.name} is non alphanumeric."
|
|
1226
|
+
)
|
|
1094
1227
|
change_type = node_resource.change_type
|
|
1095
1228
|
condition_before = Nothing
|
|
1096
1229
|
condition_after = Nothing
|
|
@@ -1127,6 +1260,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
1127
1260
|
resource_type=type_delta.before,
|
|
1128
1261
|
properties=properties_delta.before,
|
|
1129
1262
|
depends_on=depends_on_before,
|
|
1263
|
+
requires_replacement=False,
|
|
1130
1264
|
)
|
|
1131
1265
|
if change_type != ChangeType.REMOVED and is_nothing(condition_after) or condition_after:
|
|
1132
1266
|
logical_resource_id = node_resource.name
|
|
@@ -1143,6 +1277,7 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
1143
1277
|
resource_type=type_delta.after,
|
|
1144
1278
|
properties=properties_delta.after,
|
|
1145
1279
|
depends_on=depends_on_after,
|
|
1280
|
+
requires_replacement=node_resource.requires_replacement,
|
|
1146
1281
|
)
|
|
1147
1282
|
return PreprocEntityDelta(before=before, after=after)
|
|
1148
1283
|
|
|
@@ -1189,9 +1324,17 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
1189
1324
|
def visit_node_outputs(
|
|
1190
1325
|
self, node_outputs: NodeOutputs
|
|
1191
1326
|
) -> PreprocEntityDelta[list[PreprocOutput], list[PreprocOutput]]:
|
|
1192
|
-
before: list[PreprocOutput] =
|
|
1193
|
-
after: list[PreprocOutput] =
|
|
1327
|
+
before: list[PreprocOutput] = []
|
|
1328
|
+
after: list[PreprocOutput] = []
|
|
1194
1329
|
for node_output in node_outputs.outputs:
|
|
1330
|
+
if not is_nothing(node_output.condition_reference):
|
|
1331
|
+
condition_delta = self._resolve_resource_condition_reference(
|
|
1332
|
+
node_output.condition_reference
|
|
1333
|
+
)
|
|
1334
|
+
condition_after = condition_delta.after
|
|
1335
|
+
if condition_after is False:
|
|
1336
|
+
continue
|
|
1337
|
+
|
|
1195
1338
|
output_delta: PreprocEntityDelta[PreprocOutput, PreprocOutput] = self.visit(node_output)
|
|
1196
1339
|
output_before = output_delta.before
|
|
1197
1340
|
output_after = output_delta.after
|
|
@@ -1200,3 +1343,29 @@ class ChangeSetModelPreproc(ChangeSetModelVisitor):
|
|
|
1200
1343
|
if not is_nothing(output_after):
|
|
1201
1344
|
after.append(output_after)
|
|
1202
1345
|
return PreprocEntityDelta(before=before, after=after)
|
|
1346
|
+
|
|
1347
|
+
def visit_node_intrinsic_function_fn_import_value(
|
|
1348
|
+
self, node_intrinsic_function: NodeIntrinsicFunction
|
|
1349
|
+
) -> PreprocEntityDelta:
|
|
1350
|
+
def _compute_fn_import_value(string) -> str:
|
|
1351
|
+
if not isinstance(string, str):
|
|
1352
|
+
raise RuntimeError(f"Invalid parameter for import: '{string}'")
|
|
1353
|
+
|
|
1354
|
+
exports = exports_map(
|
|
1355
|
+
account_id=self._change_set.account_id, region_name=self._change_set.region_name
|
|
1356
|
+
)
|
|
1357
|
+
|
|
1358
|
+
return exports.get(string, {}).get("Value") or Nothing
|
|
1359
|
+
|
|
1360
|
+
arguments_delta = self.visit(node_intrinsic_function.arguments)
|
|
1361
|
+
delta = self._cached_apply(
|
|
1362
|
+
scope=node_intrinsic_function.scope,
|
|
1363
|
+
arguments_delta=arguments_delta,
|
|
1364
|
+
resolver=_compute_fn_import_value,
|
|
1365
|
+
)
|
|
1366
|
+
return delta
|
|
1367
|
+
|
|
1368
|
+
def visit_node_intrinsic_function_fn_transform(
|
|
1369
|
+
self, node_intrinsic_function: NodeIntrinsicFunction
|
|
1370
|
+
):
|
|
1371
|
+
raise RuntimeError("Fn::Transform should have been handled by the Transformer")
|