Skip to content

Commit 8fb860b

Browse files
authored
feat: support max allowed spec from Nacos (#1291)
1 parent 4388860 commit 8fb860b

4 files changed

Lines changed: 88 additions & 1 deletion

File tree

rock/config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,11 +751,15 @@ async def update(self):
751751
runtime_overrides = nacos_result["runtime"]
752752
if "instance_registry_mirrors" in runtime_overrides:
753753
self.runtime.instance_registry_mirrors = list(runtime_overrides["instance_registry_mirrors"] or [])
754+
max_allowed_spec = runtime_overrides.get("max_allowed_spec")
755+
if isinstance(max_allowed_spec, dict):
756+
_merge_dataclass(self.runtime.max_allowed_spec, max_allowed_spec)
754757

755758
logger.info(
756759
f"Updated config from Nacos: sandbox_config={self.sandbox_config}, proxy_service={self.proxy_service}"
757760
f", image_registry_mirrors={self.image_registry_mirrors}"
758761
f", image_mirror_lookup_allowlist={self.image_mirror_lookup_allowlist}"
759762
f", instance_registry_mirrors={self.runtime.instance_registry_mirrors}"
763+
f", max_allowed_spec={self.runtime.max_allowed_spec}"
760764
f", lifecycle={self.lifecycle}"
761765
)

rock/sandbox/sandbox_manager.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,11 @@ async def start_async(
142142
self, config: DeploymentConfig, user_info: UserInfo = {}, cluster_info: ClusterInfo = {}
143143
) -> SandboxStartResponse:
144144
await self._check_sandbox_exists_in_redis(config)
145-
self.validate_sandbox_spec(self.rock_config.runtime, config)
146145
with StageTimer("startup_timing", f"[{config.image}] Init config", logger):
147146
docker_deployment_config: DockerDeploymentConfig = await self.deployment_manager.init_config(config)
147+
# init_config refreshes the cached Nacos settings; validate only after
148+
# that refresh so a changed max_allowed_spec applies to this request.
149+
self.validate_sandbox_spec(self.rock_config.runtime, config)
148150

149151
sandbox_id = docker_deployment_config.container_name
150152
if self.rock_config.runtime.use_standard_spec_only:

tests/unit/sandbox/test_sandbox_transitions.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,24 @@ async def capture_create(sandbox_id, sandbox_info, timeout_info=None, deployment
410410

411411

412412
class TestManagerStart:
413+
@pytest.mark.asyncio
414+
async def test_refreshes_nacos_config_before_validating_spec(self, mgr_start, mock_docker_config):
415+
call_order = []
416+
417+
async def init_config(config):
418+
call_order.append("refresh")
419+
return mock_docker_config
420+
421+
def validate(runtime_config, config):
422+
call_order.append("validate")
423+
424+
mgr_start.deployment_manager.init_config.side_effect = init_config
425+
mgr_start.validate_sandbox_spec.side_effect = validate
426+
427+
await mgr_start.start_async(MagicMock(image="python:3.11"))
428+
429+
assert call_order[:2] == ["refresh", "validate"]
430+
413431
@pytest.mark.asyncio
414432
async def test_start_registers_effective_policy_before_operator_submit(
415433
self, mgr_start, mock_meta_store, mock_operator

tests/unit/test_config.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,69 @@ async def test_nacos_update_without_runtime_key_preserves_instance_mirrors():
495495
assert rock_config.runtime.instance_registry_mirrors == ["keep.example.com/ns"]
496496

497497

498+
# ===== Nacos hot-reload for max_allowed_spec =====
499+
500+
501+
@pytest.mark.asyncio
502+
async def test_nacos_update_loads_max_allowed_spec():
503+
rock_config = RockConfig()
504+
rock_config.nacos_provider = MagicMock()
505+
rock_config.nacos_provider.get_config = AsyncMock(
506+
return_value={
507+
"runtime": {
508+
"max_allowed_spec": {
509+
"cpus": 32,
510+
"memory": "128g",
511+
"disk": "512g",
512+
},
513+
},
514+
}
515+
)
516+
517+
await rock_config.update()
518+
519+
assert rock_config.runtime.max_allowed_spec.cpus == 32
520+
assert rock_config.runtime.max_allowed_spec.memory == "128g"
521+
assert rock_config.runtime.max_allowed_spec.disk == "512g"
522+
523+
524+
@pytest.mark.asyncio
525+
async def test_nacos_max_allowed_spec_partial_update_preserves_yaml_values():
526+
rock_config = RockConfig(
527+
runtime=RuntimeConfig(
528+
max_allowed_spec={
529+
"cpus": 8,
530+
"memory": "32g",
531+
"disk": "128g",
532+
}
533+
)
534+
)
535+
rock_config.nacos_provider = MagicMock()
536+
rock_config.nacos_provider.get_config = AsyncMock(
537+
return_value={"runtime": {"max_allowed_spec": {"cpus": 12}}}
538+
)
539+
540+
await rock_config.update()
541+
542+
assert rock_config.runtime.max_allowed_spec.cpus == 12
543+
assert rock_config.runtime.max_allowed_spec.memory == "32g"
544+
assert rock_config.runtime.max_allowed_spec.disk == "128g"
545+
546+
547+
@pytest.mark.asyncio
548+
async def test_nacos_without_max_allowed_spec_preserves_existing_limit():
549+
rock_config = RockConfig()
550+
rock_config.runtime.max_allowed_spec.cpus = 10
551+
rock_config.nacos_provider = MagicMock()
552+
rock_config.nacos_provider.get_config = AsyncMock(
553+
return_value={"runtime": {"instance_registry_mirrors": ["reg.example.com/ns"]}}
554+
)
555+
556+
await rock_config.update()
557+
558+
assert rock_config.runtime.max_allowed_spec.cpus == 10
559+
560+
498561
# ===== _resolve_k8s_template_includes =====
499562

500563

0 commit comments

Comments
 (0)