Skip to content

Commit bf26b24

Browse files
committed
Applied some code review suggestions
1 parent 8dbb11b commit bf26b24

File tree

6 files changed

+261
-46
lines changed

6 files changed

+261
-46
lines changed

src/zenml/cli/pipeline.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,6 @@ def deploy_snapshot(
11471147
pipeline_name_or_id: The name or ID of the pipeline.
11481148
deployment_name_or_id: Name or ID of the deployment to use for the
11491149
pipeline.
1150-
config_path: Path to pipeline configuration file.
11511150
update: If True, update the deployment with the same name if it
11521151
already exists.
11531152
overtake: If True, update the deployment with the same name if

src/zenml/deployers/docker/docker_deployer.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,8 +414,11 @@ def do_provision_deployment(
414414
auto_remove=False,
415415
ports=ports,
416416
labels={
417-
"zenml-deployment-uuid": str(deployment.id),
417+
"zenml-deployment-id": str(deployment.id),
418418
"zenml-deployment-name": deployment.name,
419+
"zenml-deployer-name": str(self.name),
420+
"zenml-deployer-id": str(self.id),
421+
"managed-by": "zenml",
419422
},
420423
extra_hosts=extra_hosts,
421424
**run_args,

src/zenml/integrations/aws/deployers/aws_deployer.py

Lines changed: 81 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@
6969
logger = get_logger(__name__)
7070

7171
# Default resource and scaling configuration constants
72-
DEFAULT_CPU = "0.25 vCPU"
73-
DEFAULT_MEMORY = "0.5 GB"
74-
DEFAULT_MIN_SIZE = 1
75-
DEFAULT_MAX_SIZE = 25
72+
DEFAULT_CPU = 0.25 # vCPU
73+
DEFAULT_MEMORY = 0.5 # GB
74+
DEFAULT_MIN_REPLICAS = 1
75+
DEFAULT_MAX_REPLICAS = 25
7676
DEFAULT_MAX_CONCURRENCY = 100
7777

7878
# AWS App Runner built-in limits
@@ -407,7 +407,7 @@ def get_tags(
407407
"""
408408
tags = {
409409
**settings.tags,
410-
"zenml-deployment-uuid": str(deployment.id),
410+
"zenml-deployment-id": str(deployment.id),
411411
"zenml-deployment-name": deployment.name,
412412
"zenml-deployer-name": str(self.name),
413413
"zenml-deployer-id": str(self.id),
@@ -473,6 +473,13 @@ def _sanitize_name(
473473
sanitized,
474474
)
475475

476+
# Remove leading and trailing extra allowed characters before truncating
477+
sanitized = re.sub(
478+
r"^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$",
479+
"",
480+
sanitized,
481+
)
482+
476483
# Truncate to fit within max_length character limit including suffix
477484
max_base_length = (
478485
max_length - len(random_suffix) - 1
@@ -1025,6 +1032,8 @@ def _requires_service_replacement(
10251032
def _convert_resource_settings_to_aws_format(
10261033
self,
10271034
resource_settings: ResourceSettings,
1035+
resource_combinations: List[Tuple[float, float]],
1036+
strict_resource_matching: bool = False,
10281037
) -> Tuple[str, str]:
10291038
"""Convert ResourceSettings to AWS App Runner resource format.
10301039
@@ -1033,6 +1042,12 @@ def _convert_resource_settings_to_aws_format(
10331042
10341043
Args:
10351044
resource_settings: The resource settings from pipeline configuration.
1045+
resource_combinations: List of supported CPU (vCPU) and memory (GB)
1046+
combinations.
1047+
strict_resource_matching: Whether to enforce strict matching of
1048+
resource requirements to AWS App Runner supported CPU and
1049+
memory combinations or approximate the closest matching
1050+
supported combination.
10361051
10371052
Returns:
10381053
Tuple of (cpu, memory) in AWS App Runner format.
@@ -1043,7 +1058,10 @@ def _convert_resource_settings_to_aws_format(
10431058
requested_memory_gb = resource_settings.get_memory(unit="GB")
10441059

10451060
cpu, memory = self._select_aws_cpu_memory_combination(
1046-
requested_cpu, requested_memory_gb
1061+
requested_cpu,
1062+
requested_memory_gb,
1063+
resource_combinations,
1064+
strict_resource_matching,
10471065
)
10481066

10491067
return cpu, memory
@@ -1052,10 +1070,12 @@ def _select_aws_cpu_memory_combination(
10521070
self,
10531071
requested_cpu: Optional[float],
10541072
requested_memory_gb: Optional[float],
1073+
resource_combinations: List[Tuple[float, float]],
1074+
strict_resource_matching: bool = False,
10551075
) -> Tuple[str, str]:
10561076
"""Select the best AWS App Runner CPU-memory combination.
10571077
1058-
AWS App Runner only supports these specific combinations:
1078+
AWS App Runner only supports specific CPU and memory combinations, e.g.:
10591079
- 0.25 vCPU: 0.5 GB, 1 GB
10601080
- 0.5 vCPU: 1 GB
10611081
- 1 vCPU: 2 GB, 3 GB, 4 GB
@@ -1067,36 +1087,37 @@ def _select_aws_cpu_memory_combination(
10671087
Args:
10681088
requested_cpu: Requested CPU count (can be None)
10691089
requested_memory_gb: Requested memory in GB (can be None)
1090+
resource_combinations: List of supported CPU (vCPU) and memory (GB)
1091+
combinations.
1092+
strict_resource_matching: Whether to enforce strict matching of
1093+
resource requirements to AWS App Runner supported CPU and
1094+
memory combinations or approximate the closest matching
1095+
supported combination.
10701096
10711097
Returns:
1072-
Tuple of (cpu, memory) that best matches requirements
1098+
Tuple of (cpu, memory) that best matches requirements, in AWS App
1099+
Runner format.
10731100
"""
1074-
valid_combinations = [
1075-
# (cpu_value, cpu_string, memory_value, memory_string)
1076-
(0.25, "0.25 vCPU", 0.5, "0.5 GB"),
1077-
(0.25, "0.25 vCPU", 1.0, "1 GB"),
1078-
(0.5, "0.5 vCPU", 1.0, "1 GB"),
1079-
(1.0, "1 vCPU", 2.0, "2 GB"),
1080-
(1.0, "1 vCPU", 3.0, "3 GB"),
1081-
(1.0, "1 vCPU", 4.0, "4 GB"),
1082-
(2.0, "2 vCPU", 4.0, "4 GB"),
1083-
(2.0, "2 vCPU", 6.0, "6 GB"),
1084-
(4.0, "4 vCPU", 8.0, "8 GB"),
1085-
(4.0, "4 vCPU", 10.0, "10 GB"),
1086-
(4.0, "4 vCPU", 12.0, "12 GB"),
1087-
]
1088-
10891101
if requested_cpu is None and requested_memory_gb is None:
1090-
return DEFAULT_CPU, DEFAULT_MEMORY
1102+
return f"{DEFAULT_CPU:g} vCPU", f"{DEFAULT_MEMORY:g} GB"
1103+
1104+
sorted_combinations = sorted(resource_combinations)
10911105

10921106
best_combination = None
1107+
exact_match = False
10931108
best_score = float("inf") # Lower is better
10941109

1095-
for cpu_val, cpu_str, mem_val, mem_str in valid_combinations:
1110+
for cpu_val, mem_val in sorted_combinations:
10961111
cpu_ok = requested_cpu is None or cpu_val >= requested_cpu
10971112
mem_ok = (
10981113
requested_memory_gb is None or mem_val >= requested_memory_gb
10991114
)
1115+
exact_match = (
1116+
cpu_val == requested_cpu and mem_val == requested_memory_gb
1117+
)
1118+
if exact_match:
1119+
best_combination = (cpu_val, mem_val)
1120+
break
11001121

11011122
if cpu_ok and mem_ok:
11021123
# Calculate "waste" score (how much over-provisioning)
@@ -1114,13 +1135,27 @@ def _select_aws_cpu_memory_combination(
11141135

11151136
if score < best_score:
11161137
best_score = score
1117-
best_combination = (cpu_str, mem_str)
1138+
best_combination = (cpu_val, mem_val)
11181139

11191140
# If no combination satisfies requirements, use the highest available
11201141
if best_combination is None:
1121-
return "4 vCPU", "12 GB"
1142+
best_combination = sorted_combinations[-1]
1143+
1144+
result = (
1145+
f"{best_combination[0]:g} vCPU",
1146+
f"{best_combination[1]:g} GB",
1147+
)
1148+
1149+
if strict_resource_matching and not exact_match:
1150+
raise ValueError(
1151+
f"Requested resource requirements ({requested_cpu} vCPU, "
1152+
f"{requested_memory_gb} GB) cannot be matched to any of the "
1153+
f"supported combinations for the AWS App Runner service. "
1154+
f"The closest matching combination is {result[0]} and "
1155+
f"{result[1]}."
1156+
)
11221157

1123-
return best_combination
1158+
return result
11241159

11251160
def _convert_scaling_settings_to_aws_format(
11261161
self,
@@ -1132,22 +1167,23 @@ def _convert_scaling_settings_to_aws_format(
11321167
resource_settings: The resource settings from pipeline configuration.
11331168
11341169
Returns:
1135-
Tuple of (min_size, max_size, max_concurrency) for AWS App Runner.
1170+
Tuple of (min_replicas, max_replicas, max_concurrency) for AWS App
1171+
Runner.
11361172
"""
1137-
min_size = DEFAULT_MIN_SIZE
1173+
min_replicas = DEFAULT_MIN_REPLICAS
11381174
if resource_settings.min_replicas is not None:
1139-
min_size = max(
1175+
min_replicas = max(
11401176
1, resource_settings.min_replicas
11411177
) # AWS App Runner min is 1
11421178

1143-
max_size = DEFAULT_MAX_SIZE
1179+
max_replicas = DEFAULT_MAX_REPLICAS
11441180
if resource_settings.max_replicas is not None:
11451181
# ResourceSettings uses 0 to mean "no limit"
11461182
# AWS App Runner needs a specific value, so we use the platform maximum
11471183
if resource_settings.max_replicas == 0:
1148-
max_size = AWS_APP_RUNNER_MAX_SIZE
1184+
max_replicas = AWS_APP_RUNNER_MAX_SIZE
11491185
else:
1150-
max_size = min(
1186+
max_replicas = min(
11511187
resource_settings.max_replicas, AWS_APP_RUNNER_MAX_SIZE
11521188
)
11531189

@@ -1158,7 +1194,7 @@ def _convert_scaling_settings_to_aws_format(
11581194
AWS_APP_RUNNER_MAX_CONCURRENCY,
11591195
)
11601196

1161-
return min_size, max_size, max_concurrency
1197+
return min_replicas, max_replicas, max_concurrency
11621198

11631199
def do_provision_deployment(
11641200
self,
@@ -1199,6 +1235,8 @@ def do_provision_deployment(
11991235

12001236
cpu, memory = self._convert_resource_settings_to_aws_format(
12011237
resource_settings,
1238+
self.config.resource_combinations,
1239+
settings.strict_resource_matching,
12021240
)
12031241
min_size, max_size, max_concurrency = (
12041242
self._convert_scaling_settings_to_aws_format(
@@ -1258,12 +1296,14 @@ def do_provision_deployment(
12581296
elif "amazonaws.com" in image:
12591297
image_repo_type = "ECR"
12601298
else:
1261-
image_repo_type = "ECR_PUBLIC" # Default fallback
1262-
logger.warning(
1263-
"App Runner only supports ECR and ECR public repositories and "
1264-
f"the container image '{image}' does not appear to be hosted on "
1265-
"either of them. Proceeding with the deployment, but be warned "
1266-
"that the App Runner service will probably fail."
1299+
raise DeploymentProvisionError(
1300+
f"AWS App Runner only supports Amazon ECR and ECR Public "
1301+
f"repositories. The container image '{image}' does not appear "
1302+
f"to be hosted on either platform. Supported image repositories:\n"
1303+
f"- ECR Public: public.ecr.aws/...\n"
1304+
f"- ECR Private: *.amazonaws.com/...\n"
1305+
f"Please push your image to one of these registries before "
1306+
f"deploying to App Runner."
12671307
)
12681308

12691309
image_config: Dict[str, Any] = {

src/zenml/integrations/aws/flavors/aws_deployer_flavor.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# permissions and limitations under the License.
1414
"""AWS App Runner deployer flavor."""
1515

16-
from typing import TYPE_CHECKING, Dict, Optional, Type
16+
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Type
1717

1818
from pydantic import Field
1919

@@ -73,6 +73,7 @@ class AWSDeployerSettings(BaseDeployerSettings):
7373
health_check_protocol: str = Field(
7474
default="TCP",
7575
description="Health check protocol. Options: 'TCP', 'HTTP'.",
76+
pattern="^TCP|HTTP$",
7677
)
7778

7879
health_check_timeout_seconds: int = Field(
@@ -178,13 +179,79 @@ class AWSDeployerSettings(BaseDeployerSettings):
178179
"Example: {'LATEST': 80, 'my-stable-revision': 20}",
179180
)
180181

182+
# Resource matching
183+
strict_resource_matching: bool = Field(
184+
default=False,
185+
description="Whether to enforce strict matching of resource requirements "
186+
"to AWS App Runner supported CPU (vCPU) and memory (GB) combinations. "
187+
"When True, raises an error if no exact match is found. When False, "
188+
"automatically selects the closest matching supported combination. "
189+
"See https://docs.aws.amazon.com/apprunner/latest/dg/architecture.html#architecture.vcpu-memory "
190+
"for more details.",
191+
)
192+
193+
194+
# AWS App Runner supported CPU (vCPU) and memory (GB) combinations
195+
DEFAULT_RESOURCE_COMBINATIONS = [
196+
(
197+
0.25,
198+
0.5,
199+
),
200+
(
201+
0.25,
202+
1.0,
203+
),
204+
(
205+
0.5,
206+
1.0,
207+
),
208+
(
209+
1.0,
210+
2.0,
211+
),
212+
(
213+
1.0,
214+
3.0,
215+
),
216+
(
217+
1.0,
218+
4.0,
219+
),
220+
(
221+
2.0,
222+
4.0,
223+
),
224+
(
225+
2.0,
226+
6.0,
227+
),
228+
(
229+
4.0,
230+
8.0,
231+
),
232+
(
233+
4.0,
234+
10.0,
235+
),
236+
(
237+
4.0,
238+
12.0,
239+
),
240+
]
241+
181242

182243
class AWSDeployerConfig(
183244
BaseDeployerConfig,
184245
AWSDeployerSettings,
185246
):
186247
"""Configuration for the AWS App Runner deployer."""
187248

249+
resource_combinations: List[Tuple[float, float]] = Field(
250+
default=DEFAULT_RESOURCE_COMBINATIONS,
251+
description="AWS App Runner supported CPU (vCPU), memory (GB) "
252+
"combinations.",
253+
)
254+
188255
@property
189256
def is_remote(self) -> bool:
190257
"""Checks if this stack component is running remotely.

src/zenml/integrations/gcp/deployers/gcp_deployer.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ def get_labels(
382382
"""
383383
return {
384384
**settings.labels,
385-
"zenml-deployment-uuid": str(deployment.id),
385+
"zenml-deployment-id": str(deployment.id),
386386
"zenml-deployment-name": deployment.name,
387387
"zenml-deployer-name": str(self.name),
388388
"zenml-deployer-id": str(self.id),
@@ -432,6 +432,13 @@ def _sanitize_name(
432432
# Remove consecutive hyphens
433433
sanitized = re.sub(r"-+", "-", sanitized)
434434

435+
# Remove leading and trailing hyphens before truncating
436+
sanitized = re.sub(
437+
r"^[^a-zA-Z0-9]+|[^a-zA-Z0-9]+$",
438+
"",
439+
sanitized,
440+
)
441+
435442
# Truncate to fit within max_length character limit including suffix
436443
max_base_length = (
437444
max_length - len(random_suffix) - 1 # -1 for the hyphen
@@ -492,7 +499,7 @@ def _get_secret_name(
492499
The Secret Manager secret name.
493500
"""
494501
deployment_id_short = str(deployment_id)[:8]
495-
raw_name = f"{prefix}_{env_var_name}"
502+
raw_name = f"{prefix}{env_var_name}"
496503

497504
return self._sanitize_name(
498505
raw_name, deployment_id_short, max_length=255

0 commit comments

Comments
 (0)