Skip to content

Commit f542481

Browse files
authored
feat(deployments): reconcile controller and prerequisite DAG (AIRCORE-758) (#315)
* feat(deployments): add reconcile controller and prerequisite DAG (#758) Introduce DeploymentsController with deployment/volume reconcilers, prerequisite gating, drift recovery, and orphan cleanup on top of the 755 plugin scaffold. Stacks on PR #280 (AIRCORE-755). AIRCORE-758 Signed-off-by: Tyler Bray <tbray@nvidia.com> * fix(deployments): address remaining PR #315 CodeRabbit review items Re-raise NemoEntityConflictError in deployment reconciler create/drift/save paths, gate Docker integration tests on BACKEND_CLASSES, document split list-health signals in README, and add ge=0 validation on DriftRecoveryPolicy. AIRCORE-758 Signed-off-by: Tyler Bray <tbray@nvidia.com> * chore(deployments): regenerate plugin OpenAPI spec for drift recovery fields The lint-web-sdk check failed because plugins/nemo-deployments/openapi/openapi.yaml was out of sync with the updated DriftRecoveryPolicy and Prerequisite models. Signed-off-by: Tyler Bray <tbray@nvidia.com> * fix(deployments): address PR #315 review feedback from benmccown and CodeRabbit Retry backend delete failures in DELETING state, rename drift/orphan config fields, rename listing helpers to entity_client, trim README to Controller section, and apply remaining review nits (docstrings, RuntimeError guards, substrate wording, test coverage). AIRCORE-758 Signed-off-by: Tyler Bray <tbray@nvidia.com> --------- Signed-off-by: Tyler Bray <tbray@nvidia.com>
1 parent 31e34e0 commit f542481

27 files changed

Lines changed: 2425 additions & 17 deletions

‎plugins/nemo-deployments/README.md‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
# NeMo Deployments Plugin
22

3-
Substrate-agnostic deployment lifecycle for the NeMo Platform: entity schemas,
4-
CRUD APIs, a `DeploymentBackend` ABC, and an executor registry.
3+
Backend-agnostic deployment lifecycle for the NeMo Platform: entity schemas,
4+
CRUD APIs, a `DeploymentBackend` ABC, an executor registry, and a background
5+
reconcile controller (`DeploymentsController`).
56

6-
## Tests
7+
## Controller
78

8-
```bash
9-
uv sync
10-
uv run pytest plugins/nemo-deployments/tests/unit -v
11-
```
9+
Register `DeploymentsController` via the `nemo.controllers` entry point. The controller
10+
paginates non-terminal deployment/volume lists, reconciles volumes before deployments,
11+
gates deployment create on mounted volumes reaching `BOUND`, and writes status via the
12+
entity client (including endpoints and status history). Orphan backend resource cleanup
13+
runs on a configurable interval and is skipped when the deployment list is unhealthy.
14+
15+
The controller exposes `is_healthy`, which is `False` when either the deployment-list or
16+
volume-list query fails. Internally these are tracked separately so operators can tell
17+
which list query failed without losing that signal behind a single boolean.
18+
19+
Per-config drift backoff overrides live on `DeploymentConfig.driftRecovery`; unset fields
20+
fall back to `DeploymentsConfig.controller`.
21+
22+
Prerequisites are currently declared on `DeploymentConfig` and resolve to a single
23+
deployment per config name in a workspace. Multiple deployments sharing one config is
24+
unsupported until prerequisites move to the `Deployment` entity (follow-up work).

‎plugins/nemo-deployments/openapi/openapi.yaml‎

Lines changed: 28 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎plugins/nemo-deployments/pyproject.toml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ dependencies = [
1414
[project.entry-points."nemo.services"]
1515
deployments = "nemo_deployments_plugin.service:DeploymentsService"
1616

17+
[project.entry-points."nemo.controllers"]
18+
deployments = "nemo_deployments_plugin.controller:DeploymentsController"
19+
1720
[build-system]
1821
requires = ["hatchling"]
1922
build-backend = "hatchling.build"

‎plugins/nemo-deployments/src/nemo_deployments_plugin/config.py‎

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Any, ClassVar
99

1010
from nemo_platform_plugin.config import NemoConfig
11-
from pydantic import BaseModel, Field
11+
from pydantic import BaseModel, Field, model_validator
1212

1313

1414
class ExecutorConfigEntry(BaseModel):
@@ -17,6 +17,30 @@ class ExecutorConfigEntry(BaseModel):
1717
config: dict[str, Any] = Field(default_factory=dict)
1818

1919

20+
class ControllerConfig(BaseModel):
21+
"""Configuration for the deployments reconcile controller."""
22+
23+
interval_seconds: int = Field(default=5, gt=0, description="Reconciliation loop interval in seconds.")
24+
drift_recovery_max_attempts: int = Field(default=5, ge=0, description="Max drift recovery attempts before FAILED.")
25+
drift_recovery_initial_delay_seconds: int = Field(
26+
default=5, ge=0, description="Initial delay for drift recovery backoff."
27+
)
28+
drift_recovery_max_delay_seconds: int = Field(
29+
default=300, ge=0, description="Max delay cap for drift recovery backoff."
30+
)
31+
orphan_cleanup_interval_seconds: int = Field(
32+
default=30,
33+
ge=0,
34+
description="Run orphaned backend resource cleanup after this many seconds (0 disables).",
35+
)
36+
37+
@model_validator(mode="after")
38+
def _validate_backoff(self) -> ControllerConfig:
39+
if self.drift_recovery_initial_delay_seconds > self.drift_recovery_max_delay_seconds:
40+
raise ValueError("drift_recovery_initial_delay_seconds must not exceed drift_recovery_max_delay_seconds")
41+
return self
42+
43+
2044
class DeploymentsConfig(NemoConfig):
2145
plugin_name: ClassVar[str] = "deployments"
2246
plugin_description: ClassVar[str] = "Configuration for the NeMo Platform deployments plugin."
@@ -27,5 +51,6 @@ class DeploymentsConfig(NemoConfig):
2751
)
2852
default_executor: str | None = Field(
2953
default=None,
30-
description="Fallback executor when Deployment.executor is unset.",
54+
description="Default executor when Deployment.executor is unset.",
3155
)
56+
controller: ControllerConfig = Field(default_factory=ControllerConfig)

‎plugins/nemo-deployments/src/nemo_deployments_plugin/constants.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"""Shared constants for the deployments plugin."""
55

66
MANAGED_BY_LABEL = "nemo-deployments"
7-
"""Label value backends use to tag substrate resources for orphan cleanup."""
7+
"""Label value backends use to tag backend resources for orphan cleanup."""
88

99
ENTITY_TYPE_DEPLOYMENT_CONFIG = "deployments_deployment_config"
1010
ENTITY_TYPE_DEPLOYMENT = "deployments_deployment"

0 commit comments

Comments
 (0)