Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 20 additions & 7 deletions plugins/nemo-deployments/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
# NeMo Deployments Plugin

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

## Tests
## Controller

```bash
uv sync
uv run pytest plugins/nemo-deployments/tests/unit -v
```
Register `DeploymentsController` via the `nemo.controllers` entry point. The controller
paginates non-terminal deployment/volume lists, reconciles volumes before deployments,
gates deployment create on mounted volumes reaching `BOUND`, and writes status via the
entity client (including endpoints and status history). Orphan backend resource cleanup
runs on a configurable interval and is skipped when the deployment list is unhealthy.

The controller exposes `is_healthy`, which is `False` when either the deployment-list or
volume-list query fails. Internally these are tracked separately so operators can tell
which list query failed without losing that signal behind a single boolean.

Per-config drift backoff overrides live on `DeploymentConfig.driftRecovery`; unset fields
fall back to `DeploymentsConfig.controller`.

Prerequisites are currently declared on `DeploymentConfig` and resolve to a single
deployment per config name in a workspace. Multiple deployments sharing one config is
unsupported until prerequisites move to the `Deployment` entity (follow-up work).
31 changes: 28 additions & 3 deletions plugins/nemo-deployments/openapi/openapi.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions plugins/nemo-deployments/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ dependencies = [
[project.entry-points."nemo.services"]
deployments = "nemo_deployments_plugin.service:DeploymentsService"

[project.entry-points."nemo.controllers"]
deployments = "nemo_deployments_plugin.controller:DeploymentsController"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Expand Down
29 changes: 27 additions & 2 deletions plugins/nemo-deployments/src/nemo_deployments_plugin/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from typing import Any, ClassVar

from nemo_platform_plugin.config import NemoConfig
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, model_validator


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


class ControllerConfig(BaseModel):
"""Configuration for the deployments reconcile controller."""

interval_seconds: int = Field(default=5, gt=0, description="Reconciliation loop interval in seconds.")
drift_recovery_max_attempts: int = Field(default=5, ge=0, description="Max drift recovery attempts before FAILED.")
drift_recovery_initial_delay_seconds: int = Field(
default=5, ge=0, description="Initial delay for drift recovery backoff."
)
drift_recovery_max_delay_seconds: int = Field(
default=300, ge=0, description="Max delay cap for drift recovery backoff."
)
orphan_cleanup_interval_seconds: int = Field(
default=30,
ge=0,
description="Run orphaned backend resource cleanup after this many seconds (0 disables).",
)

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
@model_validator(mode="after")
def _validate_backoff(self) -> ControllerConfig:
if self.drift_recovery_initial_delay_seconds > self.drift_recovery_max_delay_seconds:
raise ValueError("drift_recovery_initial_delay_seconds must not exceed drift_recovery_max_delay_seconds")
return self


class DeploymentsConfig(NemoConfig):
plugin_name: ClassVar[str] = "deployments"
plugin_description: ClassVar[str] = "Configuration for the NeMo Platform deployments plugin."
Expand All @@ -27,5 +51,6 @@ class DeploymentsConfig(NemoConfig):
)
default_executor: str | None = Field(
default=None,
description="Fallback executor when Deployment.executor is unset.",
description="Default executor when Deployment.executor is unset.",
)
controller: ControllerConfig = Field(default_factory=ControllerConfig)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"""Shared constants for the deployments plugin."""

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

ENTITY_TYPE_DEPLOYMENT_CONFIG = "deployments_deployment_config"
ENTITY_TYPE_DEPLOYMENT = "deployments_deployment"
Expand Down
Loading