cdk-factory 0.18.15__py3-none-any.whl → 0.18.20__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.

Potentially problematic release.


This version of cdk-factory might be problematic. Click here for more details.

@@ -74,6 +74,11 @@ class AutoScalingConfig(EnhancedBaseConfig):
74
74
  """Update policy configuration"""
75
75
  return self.__config.get("update_policy")
76
76
 
77
+ @property
78
+ def instance_refresh(self) -> Optional[Dict[str, Any]]:
79
+ """Instance refresh configuration for rolling updates"""
80
+ return self.__config.get("instance_refresh")
81
+
77
82
  @property
78
83
  def user_data_commands(self) -> List[str]:
79
84
  """User data commands to run on instance launch"""
@@ -119,6 +119,7 @@ class StandardizedSsmMixin:
119
119
  """Export multiple resource values to SSM Parameter Store."""
120
120
  params = {}
121
121
 
122
+ invalid_export_keys = []
122
123
  # Only export parameters that are explicitly configured in ssm_exports
123
124
  if not hasattr(config, 'ssm_exports') or not config.ssm_exports:
124
125
  logger.debug("No SSM exports configured")
@@ -138,8 +139,17 @@ class StandardizedSsmMixin:
138
139
  )
139
140
  params[key] = param
140
141
  else:
142
+ invalid_export_keys.append(key)
141
143
  logger.warning(f"SSM export configured for '{key}' but no value found in resource_values")
142
144
 
145
+ if invalid_export_keys:
146
+ message = f"Export SSM Error\n🚨 SSM exports configured for '{invalid_export_keys}' but no values found in resource_values"
147
+ available_keys = list(resource_values.keys())
148
+ message = f"{message}\n✅ Available keys: {available_keys}"
149
+ message = f"{message}\n👉 Please update to the correct key or remove from the export list."
150
+ logger.warning(message)
151
+ raise ValueError(message)
152
+
143
153
  return params
144
154
 
145
155
  def normalize_resource_name(self, name: str, for_export: bool = False) -> str:
@@ -241,11 +241,12 @@ class AutoScalingStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
241
241
  user_data = ec2.UserData.for_linux()
242
242
 
243
243
  # Add basic setup commands
244
- user_data.add_commands(
245
- "#!/bin/bash",
246
- "yum update -y",
247
- "yum install -y aws-cfn-bootstrap",
248
- )
244
+ # this will break amazon linux 2023 which uses dnf instead of yum
245
+ # user_data.add_commands(
246
+ # "#!/bin/bash",
247
+ # "yum update -y",
248
+ # "yum install -y aws-cfn-bootstrap",
249
+ # )
249
250
 
250
251
  # Add user data commands from configuration
251
252
  if self.asg_config.user_data_commands:
@@ -423,6 +424,10 @@ class AutoScalingStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
423
424
  ],
424
425
  )
425
426
 
427
+ # Add instance refresh if configured
428
+ if self.asg_config.instance_refresh:
429
+ self._configure_instance_refresh(auto_scaling_group)
430
+
426
431
  # Attach target groups if configured
427
432
  self._attach_target_groups(auto_scaling_group)
428
433
 
@@ -528,6 +533,50 @@ class AutoScalingStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
528
533
 
529
534
  logger.info(f"Exported SSM parameters: {exported_params}")
530
535
 
536
+ def _configure_instance_refresh(self, asg: autoscaling.AutoScalingGroup) -> None:
537
+ """Configure instance refresh for rolling updates"""
538
+ instance_refresh_config = self.asg_config.instance_refresh
539
+
540
+ if not instance_refresh_config.get("enabled", False):
541
+ return
542
+
543
+ # Get the CloudFormation ASG resource
544
+ cfn_asg = asg.node.default_child
545
+
546
+ # Configure instance refresh properties
547
+ refresh_props = {}
548
+
549
+ if "min_healthy_percentage" in instance_refresh_config:
550
+ refresh_props["min_healthy_percentage"] = instance_refresh_config["min_healthy_percentage"]
551
+
552
+ if "instance_warmup" in instance_refresh_config:
553
+ refresh_props["instance_warmup"] = instance_refresh_config["instance_warmup"]
554
+
555
+ if "preferences" in instance_refresh_config:
556
+ preferences = instance_refresh_config["preferences"]
557
+
558
+ # Build preferences property
559
+ pref_props = {}
560
+
561
+ if "skip_matching" in preferences:
562
+ pref_props["skip_matching"] = preferences["skip_matching"]
563
+
564
+ if "auto_rollback" in preferences:
565
+ pref_props["auto_rollback"] = preferences["auto_rollback"]
566
+
567
+ if "instance_warmup" in preferences:
568
+ pref_props["instance_warmup"] = preferences["instance_warmup"]
569
+
570
+ if "min_healthy_percentage" in preferences:
571
+ pref_props["min_healthy_percentage"] = preferences["min_healthy_percentage"]
572
+
573
+ if pref_props:
574
+ refresh_props["preferences"] = pref_props
575
+
576
+ if refresh_props:
577
+ cfn_asg.add_property_override("InstanceRefresh", refresh_props)
578
+ logger.info(f"Configured instance refresh: {refresh_props}")
579
+
531
580
 
532
581
  # Backward compatibility alias
533
582
  AutoScalingStackStandardized = AutoScalingStack
@@ -456,44 +456,42 @@ class LoadBalancerStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
456
456
  # Configure conditions
457
457
  conditions = []
458
458
 
459
- # Path patterns
460
- path_patterns = rule_config.get("path_patterns", [])
461
- if path_patterns:
462
- conditions.append(elbv2.ListenerCondition.path_patterns(path_patterns))
463
-
464
- # Host headers
465
- host_headers = rule_config.get("host_headers", [])
466
- if host_headers:
467
- conditions.append(elbv2.ListenerCondition.host_headers(host_headers))
468
-
469
- # HTTP headers
470
- http_headers = rule_config.get("http_headers", {})
471
- for header_name, header_values in http_headers.items():
472
- conditions.append(
473
- elbv2.ListenerCondition.http_header(header_name, header_values)
474
- )
475
-
476
- # Query strings
477
- query_strings = rule_config.get("query_strings", [])
478
- if query_strings:
479
- query_string_conditions = []
480
- for qs in query_strings:
481
- query_string_conditions.append(
482
- elbv2.QueryStringCondition(
483
- key=qs.get("key"), value=qs.get("value")
459
+ # Parse AWS ALB conditions format
460
+ aws_conditions = rule_config.get("conditions", [])
461
+ for condition in aws_conditions:
462
+ field = condition.get("field")
463
+ if field == "http-header" and "http_header_config" in condition:
464
+ header_config = condition["http_header_config"]
465
+ header_name = header_config.get("header_name")
466
+ header_values = header_config.get("values", [])
467
+ if header_name and header_values:
468
+ conditions.append(
469
+ elbv2.ListenerCondition.http_header(header_name, header_values)
484
470
  )
485
- )
486
- conditions.append(
487
- elbv2.ListenerCondition.query_strings(query_string_conditions)
488
- )
471
+ elif field == "path-pattern" and "values" in condition:
472
+ path_patterns = condition.get("values", [])
473
+ if path_patterns:
474
+ conditions.append(elbv2.ListenerCondition.path_patterns(path_patterns))
475
+ elif field == "host-header" and "values" in condition:
476
+ host_headers = condition.get("values", [])
477
+ if host_headers:
478
+ conditions.append(elbv2.ListenerCondition.host_headers(host_headers))
489
479
 
490
480
  # Configure actions
491
- target_group_name = rule_config.get("target_group")
492
- target_group = (
493
- self.target_groups.get(target_group_name) if target_group_name else None
494
- )
481
+ target_group = None
482
+
483
+ # Parse AWS ALB actions format
484
+ aws_actions = rule_config.get("actions", [])
485
+ for action in aws_actions:
486
+ if action.get("type") == "forward":
487
+ target_group_name = action.get("target_group")
488
+ target_group = (
489
+ self.target_groups.get(target_group_name) if target_group_name else None
490
+ )
491
+ break # Use first forward action
495
492
 
496
- if target_group:
493
+ # Validate that we have both conditions and target group before creating rule
494
+ if target_group and conditions:
497
495
  # Create rule with forward action
498
496
  elbv2.ApplicationListenerRule(
499
497
  self,
@@ -503,6 +501,15 @@ class LoadBalancerStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
503
501
  conditions=conditions,
504
502
  target_groups=[target_group],
505
503
  )
504
+ elif not conditions:
505
+ logger.warning(
506
+ f"Skipping listener rule '{rule_id}' - no conditions defined. "
507
+ f"CDK requires at least one condition for every rule."
508
+ )
509
+ elif not target_group:
510
+ logger.warning(
511
+ f"Skipping listener rule '{rule_id}' - no valid target group found."
512
+ )
506
513
 
507
514
  def _add_ip_whitelist_rules(
508
515
  self,
cdk_factory/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.18.15"
1
+ __version__ = "0.18.20"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cdk_factory
3
- Version: 0.18.15
3
+ Version: 0.18.20
4
4
  Summary: CDK Factory. A QuickStarter and best practices setup for CDK projects
5
5
  Author-email: Eric Wilson <eric.wilson@geekcafe.com>
6
6
  License: MIT License
@@ -2,7 +2,7 @@ cdk_factory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  cdk_factory/app.py,sha256=RnX0-pwdTAPAdKJK_j13Zl8anf9zYKBwboR0KA8K8xM,10346
3
3
  cdk_factory/cdk.json,sha256=SKZKhJ2PBpFH78j-F8S3VDYW-lf76--Q2I3ON-ZIQfw,3106
4
4
  cdk_factory/cli.py,sha256=FGbCTS5dYCNsfp-etshzvFlGDCjC28r6rtzYbe7KoHI,6407
5
- cdk_factory/version.py,sha256=hvl13OXw7KVmqmEI-w2lh8gFaEax1htj1PTjeMvE758,24
5
+ cdk_factory/version.py,sha256=w3jIFt0pGHQrF-Aew_QB3wXbn41ljYgrPStTZxnKK6M,24
6
6
  cdk_factory/builds/README.md,sha256=9BBWd7bXpyKdMU_g2UljhQwrC9i5O_Tvkb6oPvndoZk,90
7
7
  cdk_factory/commands/command_loader.py,sha256=QbLquuP_AdxtlxlDy-2IWCQ6D-7qa58aphnDPtp_uTs,3744
8
8
  cdk_factory/configurations/base_config.py,sha256=eJ3Pl3GWk1jVr_bYQaaWlw4_-ZiFGaiXllI_fOOX1i0,9323
@@ -21,7 +21,7 @@ cdk_factory/configurations/resources/_resources.py,sha256=tnXGn4kEC0JPQaTWB3QpAZ
21
21
  cdk_factory/configurations/resources/acm.py,sha256=HsHwiH15p0zTqRGDErn35kBEa3rkaRWTDJzVequR9oY,3062
22
22
  cdk_factory/configurations/resources/api_gateway.py,sha256=-k4hMGszIdQLb5DGmWBIPy49YGutp8zczafRh-Vob0I,4904
23
23
  cdk_factory/configurations/resources/apigateway_route_config.py,sha256=6ytn_nwKwlfpBtHL5sV6gxMpgAJ3p6QFGumMoW4CTHM,2351
24
- cdk_factory/configurations/resources/auto_scaling.py,sha256=jga8qmNY_Ctk5CWY43Bz_gMEFvN6rwKLkyAl9d5d-ko,6000
24
+ cdk_factory/configurations/resources/auto_scaling.py,sha256=mG-b1Zc7BdbdZdJp8K9EB62JY4jyjRnId_kzq1opqCM,6193
25
25
  cdk_factory/configurations/resources/cloudfront.py,sha256=YU4my1sQjLnf4DNBECS4GvPAxUG2FX_BszfO76mSbYw,3959
26
26
  cdk_factory/configurations/resources/cloudwatch_widget.py,sha256=EdEQSXUkDtoY_Mg_cJBWo1Hp84jSiK7U9tsd3k1VhKI,1271
27
27
  cdk_factory/configurations/resources/code_artifact.py,sha256=P2X2i6NEcePitEf2wkN6lFTjIbXasn0uzrlPOT0tEac,3253
@@ -66,7 +66,7 @@ cdk_factory/constructs/sqs/policies/sqs_policies.py,sha256=4p0G8G-fqNKSr68I55fvq
66
66
  cdk_factory/interfaces/istack.py,sha256=3xqGw5kNTt_KeLHdMxI7rIR0YORqcWQOqsacmDlTAv0,1167
67
67
  cdk_factory/interfaces/live_ssm_resolver.py,sha256=3FIr9a02SXqZmbFs3RT0WxczWEQR_CF7QSt7kWbDrVE,8163
68
68
  cdk_factory/interfaces/networked_stack_mixin.py,sha256=CtWT7Nhy73f0goQQG02jG7lsEucq5OrtZ0ltrp7XTFA,2884
69
- cdk_factory/interfaces/standardized_ssm_mixin.py,sha256=EcuW6Z1vBcBaK0b0nQRsmluBlr9SVHyZ_sWxVtv8JzM,25188
69
+ cdk_factory/interfaces/standardized_ssm_mixin.py,sha256=kbl8fTDSBBSUARnKjruYB7KEgsw1Xns7g6fI5Jh8Vio,25763
70
70
  cdk_factory/interfaces/vpc_provider_mixin.py,sha256=Kj0mmZd54NINprixJLs8zL-WWiSd0AQBtGdwNg8cz14,8207
71
71
  cdk_factory/lambdas/health_handler.py,sha256=dd40ykKMxWCFEIyp2ZdQvAGNjw_ylI9CSm1N24Hp2ME,196
72
72
  cdk_factory/lambdas/edge/ip_gate/handler.py,sha256=gUevgX462mqGYddtQIyJ1-Jk3oXhFmbmd46jlqjai9E,10657
@@ -86,7 +86,7 @@ cdk_factory/stack_library/acm/__init__.py,sha256=4FNRLykblcKZvq_wieYwvv9N_jgrZnJ
86
86
  cdk_factory/stack_library/acm/acm_stack.py,sha256=QJ3GkT17PmWoGkfO5Um02hvrfyJ9HbiPMnclwDP7IbA,5846
87
87
  cdk_factory/stack_library/api_gateway/api_gateway_stack.py,sha256=PvLdGvcopGpLP0FwpfUcfXNiTIfYLTXqrG-TniE38yc,39643
88
88
  cdk_factory/stack_library/auto_scaling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
- cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py,sha256=6QDVkv6Zz-qCbT6h2MU9Sp1diFXDEyjJNk1-xWMbMJg,21990
89
+ cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py,sha256=KfuNTKpDCQmvbw4F71sZBTkDFiBn1jkxxIojl21ExvQ,24140
90
90
  cdk_factory/stack_library/aws_lambdas/lambda_stack.py,sha256=SFbBPvvCopbyiuYtq-O5sQkFCf94Wzua6aDUXiFDSB4,26161
91
91
  cdk_factory/stack_library/buckets/README.md,sha256=XkK3UNVtRLE7NtUvbhCOBBYUYi8hlrrSaI1s3GJVrqI,78
92
92
  cdk_factory/stack_library/buckets/bucket_stack.py,sha256=SLoZqSffAqmeBBEVUQg54D_8Ad5UKdkjEAmKAVgAqQo,1778
@@ -103,7 +103,7 @@ cdk_factory/stack_library/ecs/ecs_service_stack.py,sha256=NWBSB-0GTWa0rHDO2o2KTI
103
103
  cdk_factory/stack_library/lambda_edge/__init__.py,sha256=ByBJ_CWdc4UtTmFBZH-6pzBMNkjkdtE65AmnB0Fs6lM,156
104
104
  cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py,sha256=C_h2Xb2_zaCGiw5otbeSozOLNbxaciMLo8FX_TQOAlw,16409
105
105
  cdk_factory/stack_library/load_balancer/__init__.py,sha256=wZpKw2OecLJGdF5mPayCYAEhu2H3c2gJFFIxwXftGDU,52
106
- cdk_factory/stack_library/load_balancer/load_balancer_stack.py,sha256=o0sxQIayyvOVBIiBrNpY5GvENqnWiYQ16_rn2CGA6yk,29900
106
+ cdk_factory/stack_library/load_balancer/load_balancer_stack.py,sha256=iiSxjuQOflF6_GLBS1xoK14fK2r47tpaI03EMvmE8V8,30693
107
107
  cdk_factory/stack_library/monitoring/__init__.py,sha256=k1G_KDx47Aw0UugaL99PN_TKlyLK4nkJVApCaAK7GJg,153
108
108
  cdk_factory/stack_library/monitoring/monitoring_stack.py,sha256=N_1YvEXE7fboH_S3kv_dSKZsufxMuPdFMjGzlNFpuSo,19283
109
109
  cdk_factory/stack_library/rds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -136,8 +136,8 @@ cdk_factory/utilities/os_execute.py,sha256=5Op0LY_8Y-pUm04y1k8MTpNrmQvcLmQHPQITE
136
136
  cdk_factory/utils/api_gateway_utilities.py,sha256=If7Xu5s_UxmuV-kL3JkXxPLBdSVUKoLtohm0IUFoiV8,4378
137
137
  cdk_factory/validation/config_validator.py,sha256=Pb0TkLiPFzUplBOgMorhRCVm08vEzZhRU5xXCDTa5CA,17602
138
138
  cdk_factory/workload/workload_factory.py,sha256=yDI3cRhVI5ELNDcJPLpk9UY54Uind1xQoV3spzT4z7E,6068
139
- cdk_factory-0.18.15.dist-info/METADATA,sha256=vb4LD2ZOosikdQobsV3l1SvopDwemZUJnaXVpxc02Fs,2452
140
- cdk_factory-0.18.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
141
- cdk_factory-0.18.15.dist-info/entry_points.txt,sha256=S1DPe0ORcdiwEALMN_WIo3UQrW_g4YdQCLEsc_b0Swg,53
142
- cdk_factory-0.18.15.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
143
- cdk_factory-0.18.15.dist-info/RECORD,,
139
+ cdk_factory-0.18.20.dist-info/METADATA,sha256=o1iNkUxex6jmTmzlO_U9hN_-XZT1JbTxLZkLVcOtfqA,2452
140
+ cdk_factory-0.18.20.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
141
+ cdk_factory-0.18.20.dist-info/entry_points.txt,sha256=S1DPe0ORcdiwEALMN_WIo3UQrW_g4YdQCLEsc_b0Swg,53
142
+ cdk_factory-0.18.20.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
143
+ cdk_factory-0.18.20.dist-info/RECORD,,