cdk-factory 0.7.30__py3-none-any.whl → 0.8.0__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.
- cdk_factory/configurations/cdk_config.py +7 -4
- cdk_factory/configurations/enhanced_base_config.py +9 -4
- cdk_factory/configurations/enhanced_ssm_config.py +9 -6
- cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py +63 -3
- cdk_factory/stack_library/api_gateway/api_gateway_stack.py +104 -2
- cdk_factory/stack_library/aws_lambdas/lambda_stack.py +121 -75
- cdk_factory/version.py +1 -1
- {cdk_factory-0.7.30.dist-info → cdk_factory-0.8.0.dist-info}/METADATA +1 -1
- {cdk_factory-0.7.30.dist-info → cdk_factory-0.8.0.dist-info}/RECORD +11 -11
- {cdk_factory-0.7.30.dist-info → cdk_factory-0.8.0.dist-info}/WHEEL +0 -0
- {cdk_factory-0.7.30.dist-info → cdk_factory-0.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -70,10 +70,13 @@ class CdkConfig:
|
|
|
70
70
|
relative_config_path = ""
|
|
71
71
|
# is this a relative path or a real path
|
|
72
72
|
if not config_path.startswith("."):
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
# Ensure both paths are absolute to avoid mixing absolute and relative paths
|
|
74
|
+
abs_config_path = os.path.abspath(config_path)
|
|
75
|
+
abs_runtime_directory = os.path.abspath(runtime_directory)
|
|
76
|
+
|
|
77
|
+
root_path = os.path.commonpath([abs_config_path, abs_runtime_directory])
|
|
78
|
+
if root_path in abs_config_path:
|
|
79
|
+
relative_config_path = abs_config_path.replace(root_path, ".")
|
|
77
80
|
|
|
78
81
|
print(f"👉 Relative Config: {relative_config_path}")
|
|
79
82
|
else:
|
|
@@ -55,10 +55,15 @@ class EnhancedBaseConfig(BaseConfig):
|
|
|
55
55
|
"""Check if SSM parameter integration is enabled"""
|
|
56
56
|
return self._enhanced_ssm.enabled if self._enhanced_ssm else False
|
|
57
57
|
|
|
58
|
+
@property
|
|
59
|
+
def ssm_workload(self) -> str:
|
|
60
|
+
"""Get the workload name for SSM parameter paths"""
|
|
61
|
+
return self._enhanced_ssm.workload if self._enhanced_ssm else "cdk-factory"
|
|
62
|
+
|
|
58
63
|
@property
|
|
59
64
|
def ssm_organization(self) -> str:
|
|
60
|
-
"""
|
|
61
|
-
return self.
|
|
65
|
+
"""Deprecated: Use ssm_workload instead. Kept for backward compatibility."""
|
|
66
|
+
return self.ssm_workload
|
|
62
67
|
|
|
63
68
|
@property
|
|
64
69
|
def ssm_environment(self) -> str:
|
|
@@ -68,7 +73,7 @@ class EnhancedBaseConfig(BaseConfig):
|
|
|
68
73
|
@property
|
|
69
74
|
def ssm_pattern(self) -> str:
|
|
70
75
|
"""Get the SSM parameter path pattern"""
|
|
71
|
-
return self._enhanced_ssm.pattern if self._enhanced_ssm else "/{
|
|
76
|
+
return self._enhanced_ssm.pattern if self._enhanced_ssm else "/{workload}/{environment}/{stack_type}/{resource_name}/{attribute}"
|
|
72
77
|
|
|
73
78
|
@property
|
|
74
79
|
def ssm_auto_export(self) -> bool:
|
|
@@ -98,7 +103,7 @@ class EnhancedBaseConfig(BaseConfig):
|
|
|
98
103
|
# Fallback to simple path generation
|
|
99
104
|
if custom_path and custom_path.startswith("/"):
|
|
100
105
|
return custom_path
|
|
101
|
-
return f"/{self.
|
|
106
|
+
return f"/{self.ssm_workload}/{self.ssm_environment}/{attribute}"
|
|
102
107
|
|
|
103
108
|
def get_export_definitions(self, context: Dict[str, Any] = None) -> List[SsmParameterDefinition]:
|
|
104
109
|
"""
|
|
@@ -42,8 +42,10 @@ class EnhancedSsmConfig:
|
|
|
42
42
|
return self.config.get("enabled", True)
|
|
43
43
|
|
|
44
44
|
@property
|
|
45
|
-
def
|
|
46
|
-
|
|
45
|
+
def workload(self) -> str:
|
|
46
|
+
"""Get workload name for SSM parameter paths (backward compatible with 'organization')"""
|
|
47
|
+
# Try 'workload' first, fall back to 'organization' for backward compatibility
|
|
48
|
+
return self.config.get("workload", self.config.get("organization", "default"))
|
|
47
49
|
|
|
48
50
|
@property
|
|
49
51
|
def environment(self) -> str:
|
|
@@ -58,7 +60,7 @@ class EnhancedSsmConfig:
|
|
|
58
60
|
def pattern(self) -> str:
|
|
59
61
|
return self.config.get(
|
|
60
62
|
"pattern",
|
|
61
|
-
"/{
|
|
63
|
+
"/{workload}/{environment}/{stack_type}/{resource_name}/{attribute}",
|
|
62
64
|
)
|
|
63
65
|
|
|
64
66
|
|
|
@@ -90,9 +92,10 @@ class EnhancedSsmConfig:
|
|
|
90
92
|
# Convert underscore attribute names to hyphen format for consistent SSM paths
|
|
91
93
|
formatted_attribute = attribute.replace("_", "-")
|
|
92
94
|
|
|
93
|
-
# Use enhanced pattern
|
|
95
|
+
# Use enhanced pattern (support both workload and organization for backward compatibility)
|
|
94
96
|
return self.pattern.format(
|
|
95
|
-
|
|
97
|
+
workload=self.workload,
|
|
98
|
+
organization=self.workload, # Backward compatibility
|
|
96
99
|
environment=self.environment,
|
|
97
100
|
stack_type=self.resource_type,
|
|
98
101
|
resource_name=self.resource_name,
|
|
@@ -236,7 +239,7 @@ class EnhancedSsmConfig:
|
|
|
236
239
|
formatted_resource_name = source_resource_name.replace("_", "-")
|
|
237
240
|
formatted_resource_type = source_resource_type.replace("_", "-")
|
|
238
241
|
|
|
239
|
-
return f"/{self.
|
|
242
|
+
return f"/{self.workload}/{self.environment}/{formatted_resource_type}/{formatted_resource_name}/{formatted_attribute}"
|
|
240
243
|
|
|
241
244
|
|
|
242
245
|
# Resource type definitions for auto-discovery
|
|
@@ -190,15 +190,23 @@ class CloudFrontDistributionConstruct(Construct):
|
|
|
190
190
|
cloudfront_config = self.stack_config.dictionary.get("cloudfront", {})
|
|
191
191
|
enable_url_rewrite = cloudfront_config.get("enable_url_rewrite", False)
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
# CloudFront only allows ONE function per event type
|
|
194
|
+
# If both URL rewrite and host restrictions are needed, combine them
|
|
195
|
+
if enable_url_rewrite and self.restrict_to_known_hosts and self.aliases:
|
|
196
|
+
function_associations.append(
|
|
197
|
+
cloudfront.FunctionAssociation(
|
|
198
|
+
function=self.__get_combined_function(hosts=self.aliases),
|
|
199
|
+
event_type=cloudfront.FunctionEventType.VIEWER_REQUEST,
|
|
200
|
+
)
|
|
201
|
+
)
|
|
202
|
+
elif enable_url_rewrite:
|
|
194
203
|
function_associations.append(
|
|
195
204
|
cloudfront.FunctionAssociation(
|
|
196
205
|
function=self.__get_url_rewrite_function(),
|
|
197
206
|
event_type=cloudfront.FunctionEventType.VIEWER_REQUEST,
|
|
198
207
|
)
|
|
199
208
|
)
|
|
200
|
-
|
|
201
|
-
if self.restrict_to_known_hosts and self.aliases:
|
|
209
|
+
elif self.restrict_to_known_hosts and self.aliases:
|
|
202
210
|
function_associations.append(
|
|
203
211
|
cloudfront.FunctionAssociation(
|
|
204
212
|
function=self.__get_cloudfront_host_restrictions(
|
|
@@ -210,6 +218,58 @@ class CloudFrontDistributionConstruct(Construct):
|
|
|
210
218
|
|
|
211
219
|
return function_associations
|
|
212
220
|
|
|
221
|
+
def __get_combined_function(self, hosts: List[str]) -> cloudfront.Function:
|
|
222
|
+
"""
|
|
223
|
+
Creates a combined CloudFront function that does both URL rewriting and host restrictions.
|
|
224
|
+
This is necessary because CloudFront only allows one function per event type.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
hosts: List of allowed hostnames
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
cloudfront.Function: Combined function
|
|
231
|
+
"""
|
|
232
|
+
allowed_hosts = "[" + ", ".join(f"'{host}'" for host in hosts) + "]"
|
|
233
|
+
|
|
234
|
+
function_code = f"""
|
|
235
|
+
function handler(event) {{
|
|
236
|
+
var request = event.request;
|
|
237
|
+
var allowedHosts = {allowed_hosts};
|
|
238
|
+
var hostHeader = request.headers.host.value;
|
|
239
|
+
|
|
240
|
+
// Check host restrictions first
|
|
241
|
+
if (allowedHosts.indexOf(hostHeader) === -1) {{
|
|
242
|
+
return {{ statusCode: 403, statusDescription: 'Forbidden' }};
|
|
243
|
+
}}
|
|
244
|
+
|
|
245
|
+
// Then do URL rewrite
|
|
246
|
+
var uri = request.uri;
|
|
247
|
+
|
|
248
|
+
// If URI doesn't have a file extension and doesn't end with /
|
|
249
|
+
if (!uri.includes('.') && !uri.endsWith('/')) {{
|
|
250
|
+
request.uri = uri + '/index.html';
|
|
251
|
+
}}
|
|
252
|
+
// If URI ends with / but not index.html
|
|
253
|
+
else if (uri.endsWith('/') && !uri.endsWith('index.html')) {{
|
|
254
|
+
request.uri = uri + 'index.html';
|
|
255
|
+
}}
|
|
256
|
+
// If URI is exactly /
|
|
257
|
+
else if (uri === '/') {{
|
|
258
|
+
request.uri = '/index.html';
|
|
259
|
+
}}
|
|
260
|
+
|
|
261
|
+
return request;
|
|
262
|
+
}}
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
combined_function = cloudfront.Function(
|
|
266
|
+
self,
|
|
267
|
+
"CombinedFunction",
|
|
268
|
+
comment="Combined URL rewrite and host restrictions for static site routing",
|
|
269
|
+
code=cloudfront.FunctionCode.from_inline(function_code),
|
|
270
|
+
)
|
|
271
|
+
return combined_function
|
|
272
|
+
|
|
213
273
|
def __get_url_rewrite_function(self) -> cloudfront.Function:
|
|
214
274
|
"""
|
|
215
275
|
Creates a CloudFront function that rewrites URLs for SPA/static site routing.
|
|
@@ -316,10 +316,112 @@ class ApiGatewayStack(IStack, EnhancedSsmParameterMixin):
|
|
|
316
316
|
def _setup_lambda_routes(self, api_gateway, api_id, routes, authorizer):
|
|
317
317
|
"""Setup Lambda routes and integrations"""
|
|
318
318
|
for route in routes:
|
|
319
|
-
|
|
319
|
+
# Check if this route references an existing Lambda via SSM
|
|
320
|
+
lambda_arn_ssm_path = route.get("lambda_arn_ssm_path")
|
|
321
|
+
lambda_name_ref = route.get("lambda_name")
|
|
322
|
+
|
|
323
|
+
if lambda_arn_ssm_path or lambda_name_ref:
|
|
324
|
+
# Import existing Lambda from SSM
|
|
325
|
+
self._setup_existing_lambda_route(api_gateway, api_id, route, authorizer)
|
|
326
|
+
else:
|
|
327
|
+
# Create new Lambda (legacy pattern)
|
|
328
|
+
self._setup_single_lambda_route(api_gateway, api_id, route, authorizer)
|
|
329
|
+
|
|
330
|
+
def _setup_existing_lambda_route(self, api_gateway, api_id, route, authorizer):
|
|
331
|
+
"""
|
|
332
|
+
Setup API Gateway route with existing Lambda function imported from SSM.
|
|
333
|
+
This is the NEW PATTERN for separating Lambda and API Gateway stacks.
|
|
334
|
+
"""
|
|
335
|
+
route_path = route["path"]
|
|
336
|
+
suffix = route_path.strip("/").replace("/", "-") or "health"
|
|
337
|
+
|
|
338
|
+
# Get Lambda ARN from SSM Parameter Store
|
|
339
|
+
lambda_arn = self._get_lambda_arn_from_ssm(route)
|
|
340
|
+
|
|
341
|
+
if not lambda_arn:
|
|
342
|
+
raise ValueError(
|
|
343
|
+
f"Could not resolve Lambda ARN for route {route_path}. "
|
|
344
|
+
f"Ensure Lambda stack has deployed and exported ARN to SSM."
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
# Import Lambda function from ARN
|
|
348
|
+
lambda_fn = _lambda.Function.from_function_arn(
|
|
349
|
+
self,
|
|
350
|
+
f"{api_id}-imported-lambda-{suffix}",
|
|
351
|
+
lambda_arn
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
logger.info(f"Imported Lambda for route {route_path}: {lambda_arn}")
|
|
355
|
+
|
|
356
|
+
# Setup API Gateway resource
|
|
357
|
+
resource = (
|
|
358
|
+
api_gateway.root.resource_for_path(route_path)
|
|
359
|
+
if route_path != "/"
|
|
360
|
+
else api_gateway.root
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
# Setup Lambda integration
|
|
364
|
+
self._setup_lambda_integration(
|
|
365
|
+
api_gateway, api_id, route, lambda_fn, authorizer, suffix
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
# Setup CORS using centralized utility
|
|
369
|
+
self.integration_utility.setup_route_cors(resource, route_path, route)
|
|
370
|
+
|
|
371
|
+
def _get_lambda_arn_from_ssm(self, route: dict) -> str:
|
|
372
|
+
"""
|
|
373
|
+
Get Lambda ARN from SSM Parameter Store.
|
|
374
|
+
Supports both explicit SSM paths and auto-discovery via lambda_name.
|
|
375
|
+
"""
|
|
376
|
+
# Option 1: Explicit SSM path provided
|
|
377
|
+
lambda_arn_ssm_path = route.get("lambda_arn_ssm_path")
|
|
378
|
+
if lambda_arn_ssm_path:
|
|
379
|
+
logger.info(f"Looking up Lambda ARN from SSM: {lambda_arn_ssm_path}")
|
|
380
|
+
try:
|
|
381
|
+
param = ssm.StringParameter.from_string_parameter_name(
|
|
382
|
+
self,
|
|
383
|
+
f"lambda-arn-param-{hash(lambda_arn_ssm_path) % 10000}",
|
|
384
|
+
lambda_arn_ssm_path
|
|
385
|
+
)
|
|
386
|
+
return param.string_value
|
|
387
|
+
except Exception as e:
|
|
388
|
+
logger.error(f"Failed to retrieve Lambda ARN from SSM path {lambda_arn_ssm_path}: {e}")
|
|
389
|
+
raise
|
|
390
|
+
|
|
391
|
+
# Option 2: Auto-discovery via lambda_name
|
|
392
|
+
lambda_name = route.get("lambda_name")
|
|
393
|
+
if lambda_name:
|
|
394
|
+
# Build SSM path using convention from lambda_stack
|
|
395
|
+
ssm_imports_config = self.stack_config.dictionary.get("api_gateway", {}).get("ssm", {}).get("imports", {})
|
|
396
|
+
# Try 'workload' first, fall back to 'organization' for backward compatibility
|
|
397
|
+
workload = ssm_imports_config.get("workload", ssm_imports_config.get("organization", self.deployment.workload_name))
|
|
398
|
+
environment = ssm_imports_config.get("environment", self.deployment.environment)
|
|
399
|
+
|
|
400
|
+
ssm_path = f"/{workload}/{environment}/lambda/{lambda_name}/arn"
|
|
401
|
+
logger.info(f"Auto-discovering Lambda ARN from SSM: {ssm_path}")
|
|
402
|
+
|
|
403
|
+
try:
|
|
404
|
+
param = ssm.StringParameter.from_string_parameter_name(
|
|
405
|
+
self,
|
|
406
|
+
f"lambda-arn-{lambda_name}-param",
|
|
407
|
+
ssm_path
|
|
408
|
+
)
|
|
409
|
+
return param.string_value
|
|
410
|
+
except Exception as e:
|
|
411
|
+
logger.error(f"Failed to auto-discover Lambda ARN for '{lambda_name}' from {ssm_path}: {e}")
|
|
412
|
+
raise ValueError(
|
|
413
|
+
f"Lambda ARN not found in SSM for '{lambda_name}'. "
|
|
414
|
+
f"Ensure the Lambda stack has deployed and exported the ARN to: {ssm_path}"
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
return None
|
|
320
418
|
|
|
321
419
|
def _setup_single_lambda_route(self, api_gateway, api_id, route, authorizer):
|
|
322
|
-
"""
|
|
420
|
+
"""
|
|
421
|
+
Setup a single Lambda route with integration and CORS.
|
|
422
|
+
LEGACY PATTERN: Creates a new Lambda function inline.
|
|
423
|
+
Prefer _setup_existing_lambda_route for new implementations.
|
|
424
|
+
"""
|
|
323
425
|
suffix = route["path"].strip("/").replace("/", "-") or "health"
|
|
324
426
|
src = route.get("src")
|
|
325
427
|
handler = route.get("handler")
|
|
@@ -34,11 +34,7 @@ from cdk_factory.configurations.resources.lambda_function import (
|
|
|
34
34
|
SQS as SQSConfig,
|
|
35
35
|
)
|
|
36
36
|
|
|
37
|
-
from
|
|
38
|
-
ApiGatewayIntegrationUtility,
|
|
39
|
-
)
|
|
40
|
-
from aws_cdk import aws_apigateway as apigateway
|
|
41
|
-
from aws_cdk import aws_cognito as cognito
|
|
37
|
+
from aws_cdk import aws_ssm as ssm
|
|
42
38
|
|
|
43
39
|
from cdk_factory.utilities.docker_utilities import DockerUtilities
|
|
44
40
|
from cdk_factory.stack.stack_module_registry import register_stack
|
|
@@ -71,8 +67,7 @@ class LambdaStack(IStack):
|
|
|
71
67
|
self.stack_config: StackConfig | None = None
|
|
72
68
|
self.deployment: DeploymentConfig | None = None
|
|
73
69
|
self.workload: WorkloadConfig | None = None
|
|
74
|
-
self.
|
|
75
|
-
self.integration_utility = None
|
|
70
|
+
self.exported_lambda_arns: dict = {} # Store exported Lambda ARNs
|
|
76
71
|
|
|
77
72
|
self.__nag_rule_suppressions()
|
|
78
73
|
|
|
@@ -90,8 +85,9 @@ class LambdaStack(IStack):
|
|
|
90
85
|
self.deployment = deployment
|
|
91
86
|
self.workload = workload
|
|
92
87
|
|
|
93
|
-
#
|
|
94
|
-
self.
|
|
88
|
+
# Check for deprecated API Gateway configuration in lambda resources
|
|
89
|
+
self.__check_for_deprecated_api_config(stack_config)
|
|
90
|
+
|
|
95
91
|
resources = stack_config.dictionary.get("resources", [])
|
|
96
92
|
if len(resources) == 0:
|
|
97
93
|
resources = stack_config.dictionary.get("lambdas", [])
|
|
@@ -100,14 +96,13 @@ class LambdaStack(IStack):
|
|
|
100
96
|
|
|
101
97
|
lambda_functions: List[LambdaFunctionConfig] = []
|
|
102
98
|
for resource in resources:
|
|
103
|
-
|
|
104
99
|
config = LambdaFunctionConfig(config=resource, deployment=deployment)
|
|
105
100
|
lambda_functions.append(config)
|
|
106
101
|
|
|
107
102
|
self.functions = self.__setup_lambdas(lambda_functions)
|
|
108
103
|
|
|
109
|
-
#
|
|
110
|
-
self.
|
|
104
|
+
# Export Lambda ARNs to SSM Parameter Store for API Gateway integration
|
|
105
|
+
self.__export_lambda_arns_to_ssm()
|
|
111
106
|
|
|
112
107
|
def __nag_rule_suppressions(self):
|
|
113
108
|
pass
|
|
@@ -191,12 +186,12 @@ class LambdaStack(IStack):
|
|
|
191
186
|
f"A resource policy for {rp.get('principal')} has not been defined"
|
|
192
187
|
)
|
|
193
188
|
|
|
194
|
-
#
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
189
|
+
# Store Lambda function for SSM export
|
|
190
|
+
self.exported_lambda_arns[function_config.name] = {
|
|
191
|
+
"arn": lambda_function.function_arn,
|
|
192
|
+
"name": lambda_function.function_name,
|
|
193
|
+
"function": lambda_function,
|
|
194
|
+
}
|
|
200
195
|
|
|
201
196
|
functions.append(lambda_function)
|
|
202
197
|
|
|
@@ -299,66 +294,117 @@ class LambdaStack(IStack):
|
|
|
299
294
|
|
|
300
295
|
rule.add_target(aws_events_targets.LambdaFunction(lambda_function))
|
|
301
296
|
|
|
302
|
-
def
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if not
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
297
|
+
def __check_for_deprecated_api_config(self, stack_config: StackConfig) -> None:
|
|
298
|
+
"""
|
|
299
|
+
Check for deprecated API Gateway configuration in lambda resources.
|
|
300
|
+
Raises an error with migration guidance if found.
|
|
301
|
+
"""
|
|
302
|
+
resources = stack_config.dictionary.get("resources", [])
|
|
303
|
+
if not resources:
|
|
304
|
+
resources = stack_config.dictionary.get("lambdas", [])
|
|
305
|
+
|
|
306
|
+
deprecated_configs = []
|
|
307
|
+
for resource in resources:
|
|
308
|
+
# Check for 'api' key in resource configuration
|
|
309
|
+
if "api" in resource or "api_gateway" in stack_config.dictionary:
|
|
310
|
+
deprecated_configs.append(resource.get("name", "unnamed"))
|
|
311
|
+
|
|
312
|
+
if deprecated_configs:
|
|
313
|
+
error_msg = f"""
|
|
314
|
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
315
|
+
║ 🚨 DEPRECATED CONFIGURATION DETECTED ║
|
|
316
|
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
317
|
+
|
|
318
|
+
Your lambda_stack configuration includes API Gateway setup, which is now
|
|
319
|
+
deprecated. This pattern caused deployment issues and violated AWS best practices.
|
|
320
|
+
|
|
321
|
+
📋 AFFECTED RESOURCES:
|
|
322
|
+
{chr(10).join(f" • {name}" for name in deprecated_configs)}
|
|
323
|
+
|
|
324
|
+
🔧 REQUIRED MIGRATION (Breaking Change v2.0+):
|
|
325
|
+
|
|
326
|
+
Lambda stacks should ONLY create Lambda functions. API Gateway integration
|
|
327
|
+
should be handled in a separate api_gateway_stack.
|
|
328
|
+
|
|
329
|
+
NEW PATTERN:
|
|
330
|
+
┌─────────────────────────────────────────────────────────────────────────────┐
|
|
331
|
+
│ 1. Lambda Stack (lambda_stack) │
|
|
332
|
+
│ • Creates Lambda functions │
|
|
333
|
+
│ • Exports Lambda ARNs to SSM Parameter Store │
|
|
334
|
+
│ • NO API Gateway configuration │
|
|
335
|
+
│ │
|
|
336
|
+
│ 2. API Gateway Stack (api_gateway_stack) │
|
|
337
|
+
│ • Imports Lambda ARNs from SSM │
|
|
338
|
+
│ • Creates API Gateway │
|
|
339
|
+
│ • Wires Lambda integrations │
|
|
340
|
+
│ • Manages stages, deployments, and domains │
|
|
341
|
+
└─────────────────────────────────────────────────────────────────────────────┘
|
|
342
|
+
|
|
343
|
+
📚 MIGRATION GUIDE:
|
|
344
|
+
https://github.com/geek-cafe/cdk-factory/blob/main/docs/MIGRATION_V2.md
|
|
345
|
+
|
|
346
|
+
💡 QUICK FIX:
|
|
347
|
+
1. Remove 'api' configuration from lambda resources
|
|
348
|
+
2. Remove 'api_gateway' from stack config
|
|
349
|
+
3. Create new api_gateway_stack configuration
|
|
350
|
+
4. Update pipeline: lambdas deploy first, then api_gateway
|
|
351
|
+
|
|
352
|
+
❓ NEED HELP?
|
|
353
|
+
See examples: cdk-factory/examples/separate-api-gateway/
|
|
354
|
+
|
|
355
|
+
════════════════════════════════════════════════════════════════════════════════
|
|
356
|
+
"""
|
|
357
|
+
raise ValueError(error_msg)
|
|
358
|
+
|
|
359
|
+
def __export_lambda_arns_to_ssm(self) -> None:
|
|
360
|
+
"""
|
|
361
|
+
Export Lambda ARNs to SSM Parameter Store for cross-stack references.
|
|
362
|
+
This enables the API Gateway stack to import and integrate with these Lambdas.
|
|
363
|
+
"""
|
|
364
|
+
if not self.exported_lambda_arns:
|
|
365
|
+
logger.info("No Lambda functions to export to SSM")
|
|
339
366
|
return
|
|
340
367
|
|
|
341
|
-
#
|
|
342
|
-
|
|
368
|
+
# Get SSM export configuration
|
|
369
|
+
ssm_config = self.stack_config.dictionary.get("ssm", {})
|
|
370
|
+
if not ssm_config.get("enabled", False):
|
|
371
|
+
logger.info("SSM export is not enabled for this stack")
|
|
372
|
+
return
|
|
343
373
|
|
|
344
|
-
#
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
374
|
+
# Build SSM parameter prefix
|
|
375
|
+
# Try 'workload' first, fall back to 'organization' for backward compatibility
|
|
376
|
+
workload = ssm_config.get("workload", ssm_config.get("organization", self.deployment.workload_name))
|
|
377
|
+
environment = ssm_config.get("environment", self.deployment.environment)
|
|
378
|
+
prefix = f"/{workload}/{environment}/lambda"
|
|
379
|
+
|
|
380
|
+
logger.info(f"Exporting {len(self.exported_lambda_arns)} Lambda functions to SSM under {prefix}")
|
|
381
|
+
|
|
382
|
+
for lambda_name, lambda_info in self.exported_lambda_arns.items():
|
|
383
|
+
# Create SSM parameter for Lambda ARN
|
|
384
|
+
param_name = f"{prefix}/{lambda_name}/arn"
|
|
385
|
+
ssm.StringParameter(
|
|
386
|
+
self,
|
|
387
|
+
f"ssm-export-{lambda_name}-arn",
|
|
388
|
+
parameter_name=param_name,
|
|
389
|
+
string_value=lambda_info["arn"],
|
|
390
|
+
description=f"Lambda ARN for {lambda_name}",
|
|
391
|
+
tier=ssm.ParameterTier.STANDARD,
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
# Also export function name for convenience
|
|
395
|
+
param_name_fname = f"{prefix}/{lambda_name}/function-name"
|
|
396
|
+
ssm.StringParameter(
|
|
397
|
+
self,
|
|
398
|
+
f"ssm-export-{lambda_name}-name",
|
|
399
|
+
parameter_name=param_name_fname,
|
|
400
|
+
string_value=lambda_info["name"],
|
|
401
|
+
description=f"Lambda function name for {lambda_name}",
|
|
402
|
+
tier=ssm.ParameterTier.STANDARD,
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
logger.info(f"✅ Exported Lambda '{lambda_name}' to SSM: {param_name}")
|
|
406
|
+
|
|
407
|
+
print(f"📤 Exported {len(self.exported_lambda_arns)} Lambda function(s) to SSM Parameter Store")
|
|
362
408
|
|
|
363
409
|
def __setup_lambda_docker_file(
|
|
364
410
|
self, lambda_config: LambdaFunctionConfig
|
cdk_factory/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.8.0"
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
cdk_factory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
cdk_factory/app.py,sha256=xv863N7O6HPKznB68_t7O4la9JacrkG87t9TjoDUk7s,2827
|
|
3
3
|
cdk_factory/cdk.json,sha256=SKZKhJ2PBpFH78j-F8S3VDYW-lf76--Q2I3ON-ZIQfw,3106
|
|
4
|
-
cdk_factory/version.py,sha256=
|
|
4
|
+
cdk_factory/version.py,sha256=iPlYCcIzuzW7T2HKDkmYlMkRI51dBLfNRxPPiWrfw9U,22
|
|
5
5
|
cdk_factory/builds/README.md,sha256=9BBWd7bXpyKdMU_g2UljhQwrC9i5O_Tvkb6oPvndoZk,90
|
|
6
6
|
cdk_factory/commands/command_loader.py,sha256=QbLquuP_AdxtlxlDy-2IWCQ6D-7qa58aphnDPtp_uTs,3744
|
|
7
7
|
cdk_factory/configurations/base_config.py,sha256=JKjhNsy0RCUZy1s8n5D_aXXI-upR9izaLtCTfKYiV9k,9624
|
|
8
|
-
cdk_factory/configurations/cdk_config.py,sha256=
|
|
8
|
+
cdk_factory/configurations/cdk_config.py,sha256=eYboaXcz9CUx4rJbEagX8ppgtIERUlopcJXii7Vn8Ws,8796
|
|
9
9
|
cdk_factory/configurations/deployment.py,sha256=LO2gd1yv1nqlX_2MIjRXptylFybWiTKsMKZU0N58rSM,12110
|
|
10
10
|
cdk_factory/configurations/deployment_wave.py,sha256=TFX7CYgr5SmLyziEb-R_OTteFWtlMHB4pT53ekV3d1Y,233
|
|
11
11
|
cdk_factory/configurations/devops.py,sha256=PG-s2ldZmMULheWdKf2lf2LSugLoKiOKyLELTZJJxu8,2506
|
|
12
|
-
cdk_factory/configurations/enhanced_base_config.py,sha256=
|
|
13
|
-
cdk_factory/configurations/enhanced_ssm_config.py,sha256=
|
|
12
|
+
cdk_factory/configurations/enhanced_base_config.py,sha256=Y1gcGZxyf_O2KFnVpCBORSGHWHqHxw0vNx_ijMad_QA,6654
|
|
13
|
+
cdk_factory/configurations/enhanced_ssm_config.py,sha256=8TRZX-gsv_fYjMKufAe2_-iF76RRE3grkC2MTYQxiJo,12718
|
|
14
14
|
cdk_factory/configurations/management.py,sha256=TSOIyxO9hGNxbgiTsS8a3pz03ungXiNqPPtZtfOpr8M,1373
|
|
15
15
|
cdk_factory/configurations/pipeline.py,sha256=3RmRP1GIk42rjYZ-A9H3357RcO13IA47N-2IQcBkySQ,4939
|
|
16
16
|
cdk_factory/configurations/pipeline_stage.py,sha256=eAT-FoIepIuv5tObk4TXlCN47FaatQO2rrFchgbMdXU,3415
|
|
@@ -46,7 +46,7 @@ cdk_factory/configurations/resources/security_group.py,sha256=8kQtaaRVEn2aDm8XoC
|
|
|
46
46
|
cdk_factory/configurations/resources/security_group_full_stack.py,sha256=x5MIMCa_olO7prFBKx9zVOfvsVdKo-2mWyhrCy27dFw,2031
|
|
47
47
|
cdk_factory/configurations/resources/sqs.py,sha256=fAh2dqttJ6PX46enFRULuiLEu3TEj0Vb2xntAOgUpYE,4346
|
|
48
48
|
cdk_factory/configurations/resources/vpc.py,sha256=sNn6w76bHFwmt6N76gZZhqpsuNB9860C1SZu6tebaXY,3835
|
|
49
|
-
cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py,sha256=
|
|
49
|
+
cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py,sha256=7uDkDbFigeUOuYgsQ9ghA4dGlx63rUCa6Iiq4RWokEA,14627
|
|
50
50
|
cdk_factory/constructs/ecr/ecr_construct.py,sha256=s0egnh4U7qgCSn_LLq1soRLWSxsFjrTx8P591AjkN84,7025
|
|
51
51
|
cdk_factory/constructs/lambdas/lambda_function_construct.py,sha256=SQ5SEXn4kezVAzXuv_A_JB3o_svyBXOMi-htvfB9HQs,4516
|
|
52
52
|
cdk_factory/constructs/lambdas/lambda_function_docker_construct.py,sha256=aSyn3eh1YnuIahZ7CbZ5WswwPL8u70ZibMoS24QQifc,9907
|
|
@@ -73,10 +73,10 @@ cdk_factory/stack/stack_module_registry.py,sha256=J14-A75VZESzRQa8p-Fepdap7Z8T7m
|
|
|
73
73
|
cdk_factory/stack/stack_modules.py,sha256=kgEK-j0smZPozVwTCfM1g1V17EyTBT0TXAQZq4vZz0o,784
|
|
74
74
|
cdk_factory/stack_library/__init__.py,sha256=5Y9TpIe8ZK1688G60PGcuP-hM0RvYEY_3Hl2qJCJJrw,581
|
|
75
75
|
cdk_factory/stack_library/stack_base.py,sha256=tTleSFmlf26DuKVF_ytftf8P7IVWb5iex8cYfYupfvQ,4940
|
|
76
|
-
cdk_factory/stack_library/api_gateway/api_gateway_stack.py,sha256=
|
|
76
|
+
cdk_factory/stack_library/api_gateway/api_gateway_stack.py,sha256=ekfu1BaA_b-Yn7U1SlfGLyskTQXMPjjotZDkxqYeysk,36984
|
|
77
77
|
cdk_factory/stack_library/auto_scaling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
78
78
|
cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py,sha256=UsFqUb_3XPJAlmZ6F75nXna3elOggD1KuFmmdmhi0Lg,19070
|
|
79
|
-
cdk_factory/stack_library/aws_lambdas/lambda_stack.py,sha256=
|
|
79
|
+
cdk_factory/stack_library/aws_lambdas/lambda_stack.py,sha256=SFbBPvvCopbyiuYtq-O5sQkFCf94Wzua6aDUXiFDSB4,26161
|
|
80
80
|
cdk_factory/stack_library/buckets/README.md,sha256=XkK3UNVtRLE7NtUvbhCOBBYUYi8hlrrSaI1s3GJVrqI,78
|
|
81
81
|
cdk_factory/stack_library/buckets/bucket_stack.py,sha256=SLoZqSffAqmeBBEVUQg54D_8Ad5UKdkjEAmKAVgAqQo,1778
|
|
82
82
|
cdk_factory/stack_library/code_artifact/code_artifact_stack.py,sha256=vySYIjWGTdVfMcUOyJdW6gTL1maHWq9ThzfrN_rVL5A,6290
|
|
@@ -112,7 +112,7 @@ cdk_factory/utilities/lambda_function_utilities.py,sha256=j3tBdv_gC2MdEwBINDwAqY
|
|
|
112
112
|
cdk_factory/utilities/os_execute.py,sha256=5Op0LY_8Y-pUm04y1k8MTpNrmQvcLmQHPQITEP7EuSU,1019
|
|
113
113
|
cdk_factory/utils/api_gateway_utilities.py,sha256=If7Xu5s_UxmuV-kL3JkXxPLBdSVUKoLtohm0IUFoiV8,4378
|
|
114
114
|
cdk_factory/workload/workload_factory.py,sha256=yBUDGIuB8-5p_mGcVFxsD2ZoZIziak3yh3LL3JvS0M4,5903
|
|
115
|
-
cdk_factory-0.
|
|
116
|
-
cdk_factory-0.
|
|
117
|
-
cdk_factory-0.
|
|
118
|
-
cdk_factory-0.
|
|
115
|
+
cdk_factory-0.8.0.dist-info/METADATA,sha256=74xL4nv4hBAxFNfxS3OTUkb91bBl3ooiLVwxMgQpPqc,2450
|
|
116
|
+
cdk_factory-0.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
117
|
+
cdk_factory-0.8.0.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
|
|
118
|
+
cdk_factory-0.8.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|