localstack-core 4.7.1.dev49__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.
Files changed (253) hide show
  1. localstack/aws/api/cloudformation/__init__.py +18 -4
  2. localstack/aws/api/cloudwatch/__init__.py +41 -1
  3. localstack/aws/api/config/__init__.py +4 -0
  4. localstack/aws/api/core.py +6 -2
  5. localstack/aws/api/dynamodb/__init__.py +30 -0
  6. localstack/aws/api/ec2/__init__.py +1522 -65
  7. localstack/aws/api/iam/__init__.py +7 -0
  8. localstack/aws/api/kinesis/__init__.py +19 -0
  9. localstack/aws/api/kms/__init__.py +6 -0
  10. localstack/aws/api/lambda_/__init__.py +13 -0
  11. localstack/aws/api/logs/__init__.py +15 -0
  12. localstack/aws/api/redshift/__init__.py +9 -3
  13. localstack/aws/api/route53/__init__.py +5 -0
  14. localstack/aws/api/s3/__init__.py +12 -0
  15. localstack/aws/api/s3control/__init__.py +54 -0
  16. localstack/aws/api/ssm/__init__.py +2 -0
  17. localstack/aws/api/transcribe/__init__.py +17 -0
  18. localstack/aws/client.py +7 -2
  19. localstack/aws/forwarder.py +52 -5
  20. localstack/aws/handlers/analytics.py +1 -1
  21. localstack/aws/handlers/internal_requests.py +6 -1
  22. localstack/aws/handlers/logging.py +12 -2
  23. localstack/aws/handlers/metric_handler.py +41 -1
  24. localstack/aws/handlers/service.py +40 -20
  25. localstack/aws/mocking.py +2 -2
  26. localstack/aws/patches.py +2 -2
  27. localstack/aws/protocol/parser.py +459 -32
  28. localstack/aws/protocol/serializer.py +689 -69
  29. localstack/aws/protocol/service_router.py +120 -20
  30. localstack/aws/protocol/validate.py +1 -1
  31. localstack/aws/scaffold.py +1 -1
  32. localstack/aws/skeleton.py +4 -2
  33. localstack/aws/spec-patches.json +58 -0
  34. localstack/aws/spec.py +37 -16
  35. localstack/cli/exceptions.py +1 -1
  36. localstack/cli/localstack.py +6 -6
  37. localstack/cli/lpm.py +3 -4
  38. localstack/cli/plugins.py +1 -1
  39. localstack/cli/profiles.py +1 -2
  40. localstack/config.py +25 -18
  41. localstack/constants.py +4 -29
  42. localstack/dev/kubernetes/__main__.py +130 -7
  43. localstack/dev/run/configurators.py +1 -4
  44. localstack/dev/run/paths.py +1 -1
  45. localstack/dns/plugins.py +5 -1
  46. localstack/dns/server.py +13 -4
  47. localstack/logging/format.py +3 -3
  48. localstack/packages/api.py +9 -8
  49. localstack/packages/core.py +2 -2
  50. localstack/packages/plugins.py +0 -8
  51. localstack/runtime/analytics.py +3 -0
  52. localstack/runtime/hooks.py +1 -1
  53. localstack/runtime/init.py +2 -2
  54. localstack/runtime/main.py +5 -5
  55. localstack/runtime/patches.py +2 -2
  56. localstack/services/apigateway/helpers.py +1 -4
  57. localstack/services/apigateway/legacy/helpers.py +7 -8
  58. localstack/services/apigateway/legacy/integration.py +4 -3
  59. localstack/services/apigateway/legacy/invocations.py +6 -5
  60. localstack/services/apigateway/legacy/provider.py +148 -68
  61. localstack/services/apigateway/legacy/templates.py +1 -1
  62. localstack/services/apigateway/next_gen/execute_api/handlers/method_request.py +7 -2
  63. localstack/services/apigateway/next_gen/execute_api/handlers/resource_router.py +1 -2
  64. localstack/services/apigateway/next_gen/execute_api/integrations/aws.py +3 -0
  65. localstack/services/apigateway/next_gen/execute_api/integrations/http.py +3 -3
  66. localstack/services/apigateway/next_gen/execute_api/template_mapping.py +2 -2
  67. localstack/services/apigateway/next_gen/execute_api/test_invoke.py +114 -9
  68. localstack/services/apigateway/next_gen/provider.py +5 -0
  69. localstack/services/apigateway/resource_providers/aws_apigateway_resource.py +1 -1
  70. localstack/services/cloudformation/api_utils.py +4 -8
  71. localstack/services/cloudformation/cfn_utils.py +1 -1
  72. localstack/services/cloudformation/engine/entities.py +14 -4
  73. localstack/services/cloudformation/engine/template_deployer.py +6 -4
  74. localstack/services/cloudformation/engine/transformers.py +6 -4
  75. localstack/services/cloudformation/engine/v2/change_set_model.py +201 -13
  76. localstack/services/cloudformation/engine/v2/change_set_model_describer.py +52 -3
  77. localstack/services/cloudformation/engine/v2/change_set_model_executor.py +117 -76
  78. localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +205 -52
  79. localstack/services/cloudformation/engine/v2/change_set_model_transform.py +350 -116
  80. localstack/services/cloudformation/engine/v2/change_set_model_validator.py +56 -14
  81. localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +1 -0
  82. localstack/services/cloudformation/engine/v2/resolving.py +7 -5
  83. localstack/services/cloudformation/engine/yaml_parser.py +9 -2
  84. localstack/services/cloudformation/provider.py +7 -5
  85. localstack/services/cloudformation/resource_provider.py +7 -1
  86. localstack/services/cloudformation/resources.py +24149 -0
  87. localstack/services/cloudformation/service_models.py +2 -2
  88. localstack/services/cloudformation/v2/entities.py +19 -9
  89. localstack/services/cloudformation/v2/provider.py +336 -106
  90. localstack/services/cloudformation/v2/types.py +13 -7
  91. localstack/services/cloudformation/v2/utils.py +4 -1
  92. localstack/services/cloudwatch/alarm_scheduler.py +4 -1
  93. localstack/services/cloudwatch/provider.py +18 -13
  94. localstack/services/cloudwatch/provider_v2.py +25 -28
  95. localstack/services/dynamodb/packages.py +2 -1
  96. localstack/services/dynamodb/provider.py +42 -0
  97. localstack/services/dynamodb/server.py +2 -2
  98. localstack/services/dynamodb/v2/provider.py +42 -0
  99. localstack/services/ecr/resource_providers/aws_ecr_repository.py +5 -2
  100. localstack/services/edge.py +1 -1
  101. localstack/services/es/provider.py +2 -2
  102. localstack/services/events/event_rule_engine.py +31 -13
  103. localstack/services/events/models.py +4 -5
  104. localstack/services/events/provider.py +17 -14
  105. localstack/services/events/target.py +17 -9
  106. localstack/services/events/v1/provider.py +5 -5
  107. localstack/services/firehose/provider.py +14 -4
  108. localstack/services/iam/provider.py +11 -116
  109. localstack/services/iam/resources/policy_simulator.py +133 -0
  110. localstack/services/kinesis/models.py +15 -2
  111. localstack/services/kinesis/provider.py +86 -3
  112. localstack/services/kms/provider.py +14 -5
  113. localstack/services/lambda_/api_utils.py +6 -3
  114. localstack/services/lambda_/invocation/docker_runtime_executor.py +1 -1
  115. localstack/services/lambda_/invocation/event_manager.py +1 -1
  116. localstack/services/lambda_/invocation/internal_sqs_queue.py +5 -9
  117. localstack/services/lambda_/invocation/lambda_models.py +10 -7
  118. localstack/services/lambda_/invocation/lambda_service.py +5 -1
  119. localstack/services/lambda_/packages.py +1 -1
  120. localstack/services/lambda_/provider.py +4 -3
  121. localstack/services/lambda_/provider_utils.py +1 -1
  122. localstack/services/logs/provider.py +36 -19
  123. localstack/services/moto.py +2 -1
  124. localstack/services/opensearch/cluster.py +15 -7
  125. localstack/services/opensearch/packages.py +26 -7
  126. localstack/services/opensearch/provider.py +8 -2
  127. localstack/services/opensearch/versions.py +56 -7
  128. localstack/services/plugins.py +11 -7
  129. localstack/services/providers.py +10 -2
  130. localstack/services/redshift/provider.py +0 -21
  131. localstack/services/s3/constants.py +5 -2
  132. localstack/services/s3/cors.py +4 -4
  133. localstack/services/s3/models.py +1 -1
  134. localstack/services/s3/notifications.py +55 -39
  135. localstack/services/s3/presigned_url.py +35 -54
  136. localstack/services/s3/provider.py +73 -15
  137. localstack/services/s3/utils.py +42 -22
  138. localstack/services/s3/validation.py +46 -32
  139. localstack/services/s3/website_hosting.py +4 -2
  140. localstack/services/ses/provider.py +18 -8
  141. localstack/services/sns/constants.py +7 -1
  142. localstack/services/sns/executor.py +9 -2
  143. localstack/services/sns/provider.py +8 -5
  144. localstack/services/sns/publisher.py +31 -16
  145. localstack/services/sns/v2/models.py +167 -0
  146. localstack/services/sns/v2/provider.py +867 -0
  147. localstack/services/sns/v2/utils.py +130 -0
  148. localstack/services/sqs/constants.py +1 -1
  149. localstack/services/sqs/developer_api.py +205 -0
  150. localstack/services/sqs/models.py +48 -5
  151. localstack/services/sqs/provider.py +38 -311
  152. localstack/services/sqs/query_api.py +6 -2
  153. localstack/services/sqs/utils.py +121 -2
  154. localstack/services/ssm/provider.py +1 -1
  155. localstack/services/stepfunctions/asl/component/intrinsic/member.py +1 -1
  156. localstack/services/stepfunctions/asl/component/state/state_choice/comparison/comparison.py +5 -11
  157. localstack/services/stepfunctions/asl/component/state/state_choice/state_choice.py +2 -2
  158. localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +2 -2
  159. localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/state_parallel.py +1 -1
  160. localstack/services/stepfunctions/asl/component/state/state_execution/state_task/state_task.py +2 -2
  161. localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +1 -1
  162. localstack/services/stepfunctions/asl/component/state/state_pass/state_pass.py +2 -2
  163. localstack/services/stepfunctions/asl/component/state/state_succeed/state_succeed.py +1 -1
  164. localstack/services/stepfunctions/asl/component/state/state_wait/state_wait.py +1 -1
  165. localstack/services/stepfunctions/asl/eval/environment.py +1 -1
  166. localstack/services/stepfunctions/asl/jsonata/jsonata.py +1 -1
  167. localstack/services/stepfunctions/backend/execution.py +2 -1
  168. localstack/services/stores.py +1 -1
  169. localstack/services/transcribe/provider.py +6 -1
  170. localstack/state/codecs.py +61 -0
  171. localstack/state/core.py +11 -5
  172. localstack/state/pickle.py +10 -49
  173. localstack/testing/aws/cloudformation_utils.py +1 -1
  174. localstack/testing/pytest/cloudformation/fixtures.py +3 -3
  175. localstack/testing/pytest/cloudformation/transformers.py +0 -0
  176. localstack/testing/pytest/container.py +4 -5
  177. localstack/testing/pytest/fixtures.py +33 -31
  178. localstack/testing/pytest/in_memory_localstack.py +0 -4
  179. localstack/testing/pytest/marking.py +38 -11
  180. localstack/testing/pytest/stepfunctions/utils.py +4 -3
  181. localstack/testing/pytest/util.py +1 -1
  182. localstack/testing/pytest/validation_tracking.py +1 -2
  183. localstack/testing/snapshots/transformer_utility.py +6 -1
  184. localstack/utils/analytics/events.py +2 -2
  185. localstack/utils/analytics/metadata.py +6 -4
  186. localstack/utils/analytics/metrics/counter.py +8 -15
  187. localstack/utils/analytics/publisher.py +1 -2
  188. localstack/utils/analytics/service_providers.py +19 -0
  189. localstack/utils/analytics/service_request_aggregator.py +2 -2
  190. localstack/utils/archives.py +11 -11
  191. localstack/utils/asyncio.py +2 -2
  192. localstack/utils/aws/arns.py +24 -29
  193. localstack/utils/aws/aws_responses.py +8 -8
  194. localstack/utils/aws/aws_stack.py +2 -3
  195. localstack/utils/aws/dead_letter_queue.py +1 -5
  196. localstack/utils/aws/message_forwarding.py +1 -2
  197. localstack/utils/aws/request_context.py +4 -5
  198. localstack/utils/aws/resources.py +1 -1
  199. localstack/utils/aws/templating.py +1 -1
  200. localstack/utils/batch_policy.py +3 -3
  201. localstack/utils/bootstrap.py +21 -13
  202. localstack/utils/catalog/catalog.py +139 -0
  203. localstack/utils/catalog/catalog_loader.py +119 -0
  204. localstack/utils/catalog/common.py +58 -0
  205. localstack/utils/catalog/plugins.py +28 -0
  206. localstack/utils/cloudwatch/cloudwatch_util.py +5 -5
  207. localstack/utils/collections.py +7 -8
  208. localstack/utils/config_listener.py +1 -1
  209. localstack/utils/container_networking.py +2 -3
  210. localstack/utils/container_utils/container_client.py +135 -136
  211. localstack/utils/container_utils/docker_cmd_client.py +85 -69
  212. localstack/utils/container_utils/docker_sdk_client.py +69 -66
  213. localstack/utils/crypto.py +10 -10
  214. localstack/utils/diagnose.py +3 -4
  215. localstack/utils/docker_utils.py +9 -5
  216. localstack/utils/files.py +33 -13
  217. localstack/utils/functions.py +4 -3
  218. localstack/utils/http.py +11 -11
  219. localstack/utils/json.py +20 -6
  220. localstack/utils/kinesis/kinesis_connector.py +2 -1
  221. localstack/utils/net.py +15 -9
  222. localstack/utils/no_exit_argument_parser.py +2 -2
  223. localstack/utils/numbers.py +9 -2
  224. localstack/utils/objects.py +7 -6
  225. localstack/utils/patch.py +10 -3
  226. localstack/utils/run.py +12 -11
  227. localstack/utils/scheduler.py +11 -11
  228. localstack/utils/server/tcp_proxy.py +2 -2
  229. localstack/utils/serving.py +3 -4
  230. localstack/utils/strings.py +15 -16
  231. localstack/utils/sync.py +126 -1
  232. localstack/utils/tagging.py +8 -6
  233. localstack/utils/testutil.py +8 -8
  234. localstack/utils/threads.py +2 -2
  235. localstack/utils/time.py +12 -4
  236. localstack/utils/urls.py +1 -3
  237. localstack/utils/xray/traceid.py +1 -1
  238. localstack/version.py +16 -3
  239. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/METADATA +18 -14
  240. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/RECORD +248 -239
  241. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/entry_points.txt +8 -4
  242. localstack_core-4.10.1.dev12.dist-info/plux.json +1 -0
  243. localstack/packages/terraform.py +0 -46
  244. localstack/services/cloudformation/deploy.html +0 -144
  245. localstack/services/cloudformation/deploy_ui.py +0 -47
  246. localstack/services/cloudformation/plugins.py +0 -12
  247. localstack_core-4.7.1.dev49.dist-info/plux.json +0 -1
  248. {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack +0 -0
  249. {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack-supervisor +0 -0
  250. {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack.bat +0 -0
  251. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/WHEEL +0 -0
  252. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/licenses/LICENSE.txt +0 -0
  253. {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,12 @@
1
1
  import re
2
2
  from typing import Any
3
3
 
4
+ from botocore.exceptions import ParamValidationError
5
+
4
6
  from localstack.services.cloudformation.engine.v2.change_set_model import (
5
- Maybe,
6
7
  NodeIntrinsicFunction,
8
+ NodeProperty,
9
+ NodeResource,
7
10
  NodeTemplate,
8
11
  Nothing,
9
12
  is_nothing,
@@ -23,23 +26,15 @@ class ChangeSetModelValidator(ChangeSetModelPreproc):
23
26
  def visit_node_template(self, node_template: NodeTemplate):
24
27
  self.visit(node_template.mappings)
25
28
  self.visit(node_template.resources)
29
+ self.visit(node_template.parameters)
26
30
 
27
31
  def visit_node_intrinsic_function_fn_get_att(
28
32
  self, node_intrinsic_function: NodeIntrinsicFunction
29
33
  ) -> PreprocEntityDelta:
30
- arguments_delta = self.visit(node_intrinsic_function.arguments)
31
- before_arguments: Maybe[str | list[str]] = arguments_delta.before
32
- after_arguments: Maybe[str | list[str]] = arguments_delta.after
33
-
34
- before = self._before_cache.get(node_intrinsic_function.scope, Nothing)
35
- if is_nothing(before) and not is_nothing(before_arguments):
36
- before = ".".join(before_arguments)
37
-
38
- after = self._after_cache.get(node_intrinsic_function.scope, Nothing)
39
- if is_nothing(after) and not is_nothing(after_arguments):
40
- after = ".".join(after_arguments)
41
-
42
- return PreprocEntityDelta(before=before, after=after)
34
+ try:
35
+ return super().visit_node_intrinsic_function_fn_get_att(node_intrinsic_function)
36
+ except RuntimeError:
37
+ return self.visit(node_intrinsic_function.arguments)
43
38
 
44
39
  def visit_node_intrinsic_function_fn_sub(
45
40
  self, node_intrinsic_function: NodeIntrinsicFunction
@@ -139,3 +134,50 @@ class ChangeSetModelValidator(ChangeSetModelPreproc):
139
134
  if is_nothing(after) and not is_nothing(arguments_after):
140
135
  after = _compute_sub(args=arguments_after, select_before=False)
141
136
  return PreprocEntityDelta(before=before, after=after)
137
+
138
+ def visit_node_intrinsic_function_fn_transform(
139
+ self, node_intrinsic_function: NodeIntrinsicFunction
140
+ ):
141
+ # TODO Research this issue:
142
+ # Function is already resolved in the template reaching this point
143
+ # But transformation is still present in update model
144
+ return self.visit(node_intrinsic_function.arguments)
145
+
146
+ def visit_node_intrinsic_function_fn_split(
147
+ self, node_intrinsic_function: NodeIntrinsicFunction
148
+ ) -> PreprocEntityDelta:
149
+ try:
150
+ # If an argument is a Parameter it should be resolved, any other case, ignore it
151
+ return super().visit_node_intrinsic_function_fn_split(node_intrinsic_function)
152
+ except RuntimeError:
153
+ return self.visit(node_intrinsic_function.arguments)
154
+
155
+ def visit_node_intrinsic_function_fn_select(
156
+ self, node_intrinsic_function: NodeIntrinsicFunction
157
+ ) -> PreprocEntityDelta:
158
+ try:
159
+ # If an argument is a Parameter it should be resolved, any other case, ignore it
160
+ return super().visit_node_intrinsic_function_fn_select(node_intrinsic_function)
161
+ except RuntimeError:
162
+ return self.visit(node_intrinsic_function.arguments)
163
+
164
+ def visit_node_resource(self, node_resource: NodeResource) -> PreprocEntityDelta:
165
+ try:
166
+ if delta := super().visit_node_resource(node_resource):
167
+ return delta
168
+ return super().visit_node_properties(node_resource.properties)
169
+ except RuntimeError:
170
+ return super().visit_node_properties(node_resource.properties)
171
+
172
+ def visit_node_property(self, node_property: NodeProperty) -> PreprocEntityDelta:
173
+ try:
174
+ return super().visit_node_property(node_property)
175
+ except ParamValidationError:
176
+ return self.visit(node_property.value)
177
+
178
+ # ignore errors from dynamic replacements
179
+ def _maybe_perform_dynamic_replacements(self, delta: PreprocEntityDelta) -> PreprocEntityDelta:
180
+ try:
181
+ return super()._maybe_perform_dynamic_replacements(delta)
182
+ except Exception:
183
+ return delta
@@ -54,6 +54,7 @@ class ChangeSetModelVisitor(abc.ABC):
54
54
  # entities (parameters, mappings, conditions, etc.). Then compute the output fields; computing
55
55
  # only the output fields would only result in the deployment logic of the referenced outputs
56
56
  # being evaluated, hence enforce the visiting of all the resources first.
57
+ self.visit(node_template.conditions)
57
58
  self.visit(node_template.resources)
58
59
  self.visit(node_template.outputs)
59
60
 
@@ -1,3 +1,4 @@
1
+ import json
1
2
  import logging
2
3
  import re
3
4
  from dataclasses import dataclass
@@ -6,11 +7,12 @@ from typing import Any
6
7
  from botocore.exceptions import ClientError
7
8
 
8
9
  from localstack.aws.connect import connect_to
9
- from localstack.utils import json
10
10
 
11
11
  LOG = logging.getLogger(__name__)
12
12
 
13
- REGEX_DYNAMIC_REF = re.compile(r"{{resolve:([^:]+):(.+)}}")
13
+ # CloudFormation allows using dynamic references in `Fn::Sub` expressions, so we must make sure
14
+ # we don't capture the parameter usage by excluding ${} characters
15
+ REGEX_DYNAMIC_REF = re.compile(r"{{resolve:([^:]+):([^${}]+)}}")
14
16
 
15
17
 
16
18
  @dataclass
@@ -21,7 +23,7 @@ class DynamicReference:
21
23
 
22
24
  def extract_dynamic_reference(value: Any) -> DynamicReference | None:
23
25
  if isinstance(value, str):
24
- if dynamic_ref_match := REGEX_DYNAMIC_REF.match(value):
26
+ if dynamic_ref_match := REGEX_DYNAMIC_REF.search(value):
25
27
  return DynamicReference(dynamic_ref_match[1], dynamic_ref_match[2])
26
28
  return None
27
29
 
@@ -90,9 +92,9 @@ def perform_dynamic_reference_lookup(
90
92
  raise RuntimeError(
91
93
  f"JSON value for {reference.service_name}.{reference.reference_key} not present"
92
94
  )
93
- return json_secret[json_key]
95
+ return str(json_secret[json_key])
94
96
  else:
95
- return secret_value
97
+ return str(secret_value)
96
98
 
97
99
  LOG.warning(
98
100
  "Unsupported service for dynamic parameter: service_name=%s", reference.service_name
@@ -1,5 +1,7 @@
1
1
  import yaml
2
2
 
3
+ from localstack.services.cloudformation.engine.validations import ValidationError
4
+
3
5
 
4
6
  def construct_raw(_, node):
5
7
  return node.value
@@ -60,5 +62,10 @@ customloader = NoDatesSafeLoader
60
62
  yaml.add_multi_constructor("!", shorthand_constructor, customloader)
61
63
 
62
64
 
63
- def parse_yaml(input_data: str):
64
- return yaml.load(input_data, customloader)
65
+ def parse_yaml(input_data: str) -> dict:
66
+ parsed = yaml.load(input_data, Loader=customloader)
67
+
68
+ if not isinstance(parsed, dict):
69
+ raise ValidationError("Template format error: unsupported structure.")
70
+
71
+ return parsed
@@ -159,7 +159,7 @@ def find_stack_instance(stack_set: StackSet, account: str, region: str):
159
159
 
160
160
  def stack_not_found_error(stack_name: str):
161
161
  # FIXME
162
- raise ValidationError("Stack with id %s does not exist" % stack_name)
162
+ raise ValidationError(f"Stack with id {stack_name} does not exist")
163
163
 
164
164
 
165
165
  def not_found_error(message: str):
@@ -305,8 +305,8 @@ class CloudformationProvider(CloudformationApi):
305
305
  deployer.deploy_stack()
306
306
  except Exception as e:
307
307
  stack.set_stack_status("CREATE_FAILED")
308
- msg = 'Unable to create stack "%s": %s' % (stack.stack_name, e)
309
- LOG.exception("%s")
308
+ msg = f'Unable to create stack "{stack.stack_name}": {e}'
309
+ LOG.error("%s", exc_info=LOG.isEnabledFor(logging.DEBUG))
310
310
  raise ValidationError(msg) from e
311
311
 
312
312
  return CreateStackOutput(StackId=stack.stack_id)
@@ -423,7 +423,7 @@ class CloudformationProvider(CloudformationApi):
423
423
  except Exception as e:
424
424
  stack.set_stack_status("UPDATE_FAILED")
425
425
  msg = f'Unable to update stack "{stack_name}": {e}'
426
- LOG.exception("%s", msg)
426
+ LOG.error("%s", msg, exc_info=LOG.isEnabledFor(logging.DEBUG))
427
427
  raise ValidationError(msg) from e
428
428
 
429
429
  return UpdateStackOutput(StackId=stack.stack_id)
@@ -605,6 +605,8 @@ class CloudformationProvider(CloudformationApi):
605
605
  req_params = request
606
606
  change_set_type = req_params.get("ChangeSetType", "UPDATE")
607
607
  stack_name = req_params.get("StackName")
608
+ if not stack_name:
609
+ raise ValidationError("Member must have length greater than or equal to 1")
608
610
  change_set_name = req_params.get("ChangeSetName")
609
611
  template_body = req_params.get("TemplateBody")
610
612
  # s3 or secretsmanager url
@@ -1052,7 +1054,7 @@ class CloudformationProvider(CloudformationApi):
1052
1054
  Description=valid_template.get("Description"), Parameters=parameters
1053
1055
  )
1054
1056
  except Exception as e:
1055
- LOG.exception("Error validating template")
1057
+ LOG.error("Error validating template", exc_info=LOG.isEnabledFor(logging.DEBUG))
1056
1058
  raise ValidationError("Template Validation Error") from e
1057
1059
 
1058
1060
  # =======================================
@@ -518,10 +518,14 @@ class ResourceProviderExecutor:
518
518
  try:
519
519
  return resource_provider.update(request)
520
520
  except NotImplementedError:
521
+ feature_request_url = "https://github.com/localstack/localstack/issues/new?template=feature-request.yml"
521
522
  LOG.warning(
522
- 'Unable to update resource type "%s", id "%s"',
523
+ 'Unable to update resource type "%s", id "%s", '
524
+ "the update operation is not implemented for this resource. "
525
+ "Please consider submitting a feature request at this URL: %s",
523
526
  request.resource_type,
524
527
  request.logical_resource_id,
528
+ feature_request_url,
525
529
  )
526
530
  if request.previous_state is None:
527
531
  # this is an issue with our update detection. We should never be in this state.
@@ -563,6 +567,8 @@ class ResourceProviderExecutor:
563
567
  @staticmethod
564
568
  def try_load_resource_provider(resource_type: str) -> ResourceProvider | None:
565
569
  # TODO: unify namespace of plugins
570
+ if resource_type and resource_type.startswith("Custom"):
571
+ resource_type = "AWS::CloudFormation::CustomResource"
566
572
 
567
573
  # 1. try to load pro resource provider
568
574
  # prioritise pro resource providers