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
9 changes: 8 additions & 1 deletion src/dstack/_internal/core/compatibility/runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
IncludeExcludeDictType,
IncludeExcludeSetType,
)
from dstack._internal.core.models.configurations import TaskConfiguration
from dstack._internal.core.models.configurations import (
ServiceConfiguration,
TaskConfiguration,
)
from dstack._internal.core.models.runs import (
DEFAULT_REPLICA_GROUP_NAME,
ApplyRunPlanInput,
Expand Down Expand Up @@ -89,6 +92,10 @@ def get_run_spec_excludes(run_spec: RunSpec) -> IncludeExcludeDictType:
if run_spec.configuration.nodes is None:
# Omit nodes when unset so old servers never see null (pre-hetero nodes was int=1).
configuration_excludes["nodes"] = True
elif isinstance(run_spec.configuration, ServiceConfiguration):
if run_spec.configuration.groups is None:
# Servers before 0.21.3 have no service `groups` and reject it as an extra field.
configuration_excludes["groups"] = True

if configuration_excludes:
spec_excludes["configuration"] = configuration_excludes
Expand Down
26 changes: 25 additions & 1 deletion src/tests/_internal/core/models/test_runs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

from dstack._internal.core.compatibility.runs import get_run_spec_excludes
from dstack._internal.core.models.common import validate_extra_ignore
from dstack._internal.core.models.configurations import TaskConfiguration
from dstack._internal.core.models.configurations import (
ServiceConfiguration,
TaskConfiguration,
)
from dstack._internal.core.models.profiles import (
CreationPolicy,
Profile,
Expand Down Expand Up @@ -42,6 +45,27 @@ def test_unset_task_nodes_are_excluded_for_compatibility():
assert configuration_excludes["nodes"] is True


def test_unset_service_groups_are_excluded_for_compatibility():
configuration = ServiceConfiguration(commands=["true"], port=8000)

configuration_excludes = get_run_spec_excludes(RunSpec(configuration=configuration)).get(
"configuration"
)

assert isinstance(configuration_excludes, dict)
assert configuration_excludes["groups"] is True


def test_set_service_groups_are_not_excluded():
configuration = ServiceConfiguration(port=8000, groups=[{"replicas": 1, "commands": ["true"]}])

configuration_excludes = get_run_spec_excludes(RunSpec(configuration=configuration)).get(
"configuration"
)

assert not isinstance(configuration_excludes, dict) or "groups" not in configuration_excludes


def test_job_termination_reason_to_status_works_with_all_enum_variants():
for job_termination_reason in JobTerminationReason:
job_status = job_termination_reason.to_status()
Expand Down
Loading