diff --git a/docs/docs/guides/server-deployment.md b/docs/docs/guides/server-deployment.md index fb50f5592b..f1d7546d77 100644 --- a/docs/docs/guides/server-deployment.md +++ b/docs/docs/guides/server-deployment.md @@ -228,7 +228,7 @@ When using [files](../concepts/dev-environments.md#files) or [repos](../concept ### S3 -To use S3 for storing uploaded files, set the `DSTACK_SERVER_S3_BUCKET` and `DSTACK_SERVER_BUCKET_REGION` environment variables. +To use S3 for storing uploaded files, set the `DSTACK_SERVER_S3_BUCKET` and `DSTACK_SERVER_S3_BUCKET_REGION` environment variables. The bucket must be created beforehand. `dstack` won't try to create it. ??? info "Required permissions" diff --git a/src/dstack/_internal/cli/commands/offer.py b/src/dstack/_internal/cli/commands/offer.py index 33f3656d02..bc6bb0a5db 100644 --- a/src/dstack/_internal/cli/commands/offer.py +++ b/src/dstack/_internal/cli/commands/offer.py @@ -105,7 +105,6 @@ def _command(self, args: argparse.Namespace): run_spec = RunSpec( configuration=conf, profile=profile, - ssh_key_pub="(dummy)", # TODO: Remove since 0.19.40 ) if args.group_by: diff --git a/src/dstack/_internal/cli/services/configurators/fleet.py b/src/dstack/_internal/cli/services/configurators/fleet.py index 36eaaf8fe7..89278feb94 100644 --- a/src/dstack/_internal/cli/services/configurators/fleet.py +++ b/src/dstack/_internal/cli/services/configurators/fleet.py @@ -20,10 +20,8 @@ from dstack._internal.core.errors import ( CLIError, ConfigurationError, - MethodNotAllowedError, ResourceNotExistsError, ServerClientError, - URLNotFoundError, ) from dstack._internal.core.models.common import ApplyAction from dstack._internal.core.models.configurations import ApplyConfigurationType @@ -39,7 +37,6 @@ from dstack._internal.utils.common import local_time from dstack._internal.utils.logging import get_logger from dstack._internal.utils.ssh import convert_ssh_key_to_pem, generate_public_key, pkey_from_str -from dstack.api._public import Client from dstack.api.utils import load_profile logger = get_logger(__name__) @@ -233,7 +230,7 @@ def _apply_plan_on_old_server(self, plan: FleetPlan, command_args: argparse.Name try: with console.status("Applying plan..."): - fleet = _apply_plan(self.api, plan) + fleet = self.api.client.fleets.apply_plan(project_name=self.api.project, plan=plan) except ServerClientError as e: raise CLIError(e.msg) if command_args.detach: @@ -481,17 +478,3 @@ def _fleet_retrying(fleet: Fleet) -> bool: return False active_instances = [i for i in fleet.instances if i.status.is_active()] return len(active_instances) < fleet.spec.configuration.nodes.min - - -def _apply_plan(api: Client, plan: FleetPlan) -> Fleet: - try: - return api.client.fleets.apply_plan( - project_name=api.project, - plan=plan, - ) - except (URLNotFoundError, MethodNotAllowedError): - # TODO: Remove in 0.20 - return api.client.fleets.create( - project_name=api.project, - spec=plan.spec, - ) diff --git a/src/dstack/_internal/core/backends/azure/compute.py b/src/dstack/_internal/core/backends/azure/compute.py index 8573ec99ab..0089f5e478 100644 --- a/src/dstack/_internal/core/backends/azure/compute.py +++ b/src/dstack/_internal/core/backends/azure/compute.py @@ -420,11 +420,6 @@ def get_image_name(self) -> str: r"NC(\d+)ads_A100_v4", # NC A100 v4-series [A100 80GB] r"ND(\d+)asr_v4", # ND A100 v4-series [8xA100 40GB] r"ND(\d+)amsr_A100_v4", # NDm A100 v4-series [8xA100 80GB] - # Deprecated series - # TODO: Remove after several releases - r"D(\d+)s_v3", # Dsv3-series (general purpose) - r"E(\d+)i?s_v4", # Esv4-series (memory optimized) - r"E(\d+)-(\d+)s_v4", # Esv4-series (constrained vCPU) ] _SUPPORTED_VM_SERIES_PATTERN = ( "^Standard_(" + "|".join(f"({s})" for s in _SUPPORTED_VM_SERIES_PATTERNS) + ")$" diff --git a/src/dstack/_internal/core/backends/kubernetes/compute.py b/src/dstack/_internal/core/backends/kubernetes/compute.py index 71a59ad225..da5e125b96 100644 --- a/src/dstack/_internal/core/backends/kubernetes/compute.py +++ b/src/dstack/_internal/core/backends/kubernetes/compute.py @@ -34,8 +34,6 @@ from dstack._internal.core.consts import DSTACK_RUNNER_SSH_PORT from dstack._internal.core.errors import ComputeError from dstack._internal.core.models.backends.base import BackendType - -# TODO: update import as KNOWN_GPUS becomes public from dstack._internal.core.models.gateways import ( GatewayComputeConfiguration, GatewayProvisioningData, diff --git a/src/dstack/_internal/core/compatibility/fleets.py b/src/dstack/_internal/core/compatibility/fleets.py index 2ac640b27f..e22903df34 100644 --- a/src/dstack/_internal/core/compatibility/fleets.py +++ b/src/dstack/_internal/core/compatibility/fleets.py @@ -2,7 +2,6 @@ from dstack._internal.core.models.common import IncludeExcludeDictType, IncludeExcludeSetType from dstack._internal.core.models.fleets import ApplyFleetPlanInput, FleetSpec -from dstack._internal.core.models.instances import Instance def get_get_plan_excludes(fleet_spec: FleetSpec) -> IncludeExcludeDictType: @@ -22,10 +21,6 @@ def get_apply_plan_excludes(plan_input: ApplyFleetPlanInput) -> IncludeExcludeDi if current_resource is not None: current_resource_excludes = {} apply_plan_excludes["current_resource"] = current_resource_excludes - if all(map(_should_exclude_instance_cpu_arch, current_resource.instances)): - current_resource_excludes["instances"] = { - "__all__": {"instance_type": {"resources": {"cpu_arch"}}} - } return {"plan": apply_plan_excludes} @@ -46,24 +41,14 @@ def get_fleet_spec_excludes(fleet_spec: FleetSpec) -> Optional[IncludeExcludeDic spec_excludes: IncludeExcludeDictType = {} configuration_excludes: IncludeExcludeDictType = {} profile_excludes: IncludeExcludeSetType = set() - profile = fleet_spec.profile - if profile.fleets is None: - profile_excludes.add("fleets") - if fleet_spec.configuration.tags is None: - configuration_excludes["tags"] = True - if profile.tags is None: - profile_excludes.add("tags") - if profile.startup_order is None: - profile_excludes.add("startup_order") - if profile.stop_criteria is None: - profile_excludes.add("stop_criteria") - if profile.schedule is None: - profile_excludes.add("schedule") - if ( - fleet_spec.configuration.nodes - and fleet_spec.configuration.nodes.min == fleet_spec.configuration.nodes.target - ): - configuration_excludes["nodes"] = {"target"} + + # Add excludes like this: + # + # if fleet_spec.configuration.tags is None: + # configuration_excludes["tags"] = True + # if fleet_spec.profile.tags is None: + # profile_excludes.add("tags") + if configuration_excludes: spec_excludes["configuration"] = configuration_excludes if profile_excludes: @@ -71,10 +56,3 @@ def get_fleet_spec_excludes(fleet_spec: FleetSpec) -> Optional[IncludeExcludeDic if spec_excludes: return spec_excludes return None - - -def _should_exclude_instance_cpu_arch(instance: Instance) -> bool: - try: - return instance.instance_type.resources.cpu_arch is None - except AttributeError: - return True diff --git a/src/dstack/_internal/core/compatibility/gateways.py b/src/dstack/_internal/core/compatibility/gateways.py index 2b9ed18dd9..de94f6a18e 100644 --- a/src/dstack/_internal/core/compatibility/gateways.py +++ b/src/dstack/_internal/core/compatibility/gateways.py @@ -30,8 +30,10 @@ def _get_gateway_configuration_excludes( configuration: GatewayConfiguration, ) -> IncludeExcludeDictType: configuration_excludes: IncludeExcludeDictType = {} - if configuration.tags is None: - configuration_excludes["tags"] = True - if configuration.router is None: - configuration_excludes["router"] = True + + # Add excludes like this: + # + # if configuration.tags is None: + # configuration_excludes["tags"] = True + return configuration_excludes diff --git a/src/dstack/_internal/core/compatibility/logs.py b/src/dstack/_internal/core/compatibility/logs.py index 7d499047ae..078ce62218 100644 --- a/src/dstack/_internal/core/compatibility/logs.py +++ b/src/dstack/_internal/core/compatibility/logs.py @@ -11,6 +11,4 @@ def get_poll_logs_excludes(request: PollLogsRequest) -> Optional[IncludeExcludeD clients backward-compatibility with older servers. """ excludes: IncludeExcludeDictType = {} - if request.next_token is None: - excludes["next_token"] = True return excludes if excludes else None diff --git a/src/dstack/_internal/core/compatibility/runs.py b/src/dstack/_internal/core/compatibility/runs.py index 0cf9cfb9e2..5bec348d67 100644 --- a/src/dstack/_internal/core/compatibility/runs.py +++ b/src/dstack/_internal/core/compatibility/runs.py @@ -1,18 +1,12 @@ from typing import Optional from dstack._internal.core.models.common import IncludeExcludeDictType, IncludeExcludeSetType -from dstack._internal.core.models.configurations import LEGACY_REPO_DIR, ServiceConfiguration -from dstack._internal.core.models.runs import ApplyRunPlanInput, JobSpec, JobSubmission, RunSpec +from dstack._internal.core.models.runs import ApplyRunPlanInput, JobSpec, RunSpec from dstack._internal.server.schemas.runs import GetRunPlanRequest, ListRunsRequest -from dstack._internal.settings import FeatureFlags def get_list_runs_excludes(list_runs_request: ListRunsRequest) -> IncludeExcludeSetType: - excludes = set() - if list_runs_request.include_jobs: - excludes.add("include_jobs") - if list_runs_request.job_submissions_limit is None: - excludes.add("job_submissions_limit") + excludes: IncludeExcludeSetType = set() return excludes @@ -29,82 +23,7 @@ def get_apply_plan_excludes(plan: ApplyRunPlanInput) -> Optional[IncludeExcludeD current_resource = plan.current_resource if current_resource is not None: current_resource_excludes: IncludeExcludeDictType = {} - current_resource_excludes["status_message"] = True - if current_resource.deployment_num == 0: - current_resource_excludes["deployment_num"] = True - if current_resource.fleet is None: - current_resource_excludes["fleet"] = True - if current_resource.next_triggered_at is None: - current_resource_excludes["next_triggered_at"] = True - apply_plan_excludes["current_resource"] = current_resource_excludes current_resource_excludes["run_spec"] = get_run_spec_excludes(current_resource.run_spec) - job_submissions_excludes: IncludeExcludeDictType = {} - current_resource_excludes["jobs"] = { - "__all__": { - "job_spec": get_job_spec_excludes([job.job_spec for job in current_resource.jobs]), - "job_submissions": {"__all__": job_submissions_excludes}, - } - } - job_submissions = [js for j in current_resource.jobs for js in j.job_submissions] - if all(map(_should_exclude_job_submission_jpd_cpu_arch, job_submissions)): - job_submissions_excludes["job_provisioning_data"] = { - "instance_type": {"resources": {"cpu_arch"}} - } - jrd_offer_excludes = {} - if any( - js.job_runtime_data and js.job_runtime_data.offer for js in job_submissions - ) and all( - not js.job_runtime_data - or not js.job_runtime_data.offer - or not js.job_runtime_data.offer.backend_data - for js in job_submissions - ): - jrd_offer_excludes["backend_data"] = True - if all(map(_should_exclude_job_submission_jrd_cpu_arch, job_submissions)): - jrd_offer_excludes["instance"] = {"resources": {"cpu_arch"}} - if jrd_offer_excludes: - job_submissions_excludes["job_runtime_data"] = {"offer": jrd_offer_excludes} - if all(js.exit_status is None for js in job_submissions): - job_submissions_excludes["exit_status"] = True - if all(js.status_message == "" for js in job_submissions): - job_submissions_excludes["status_message"] = True - if all(js.error is None for js in job_submissions): - job_submissions_excludes["error"] = True - if all(js.deployment_num == 0 for js in job_submissions): - job_submissions_excludes["deployment_num"] = True - if all(not js.probes for js in job_submissions): - job_submissions_excludes["probes"] = True - latest_job_submission = current_resource.latest_job_submission - if latest_job_submission is not None: - latest_job_submission_excludes: IncludeExcludeDictType = {} - current_resource_excludes["latest_job_submission"] = latest_job_submission_excludes - if _should_exclude_job_submission_jpd_cpu_arch(latest_job_submission): - latest_job_submission_excludes["job_provisioning_data"] = { - "instance_type": {"resources": {"cpu_arch"}} - } - latest_job_submission_jrd_offer_excludes = {} - if ( - latest_job_submission.job_runtime_data - and latest_job_submission.job_runtime_data.offer - and not latest_job_submission.job_runtime_data.offer.backend_data - ): - latest_job_submission_jrd_offer_excludes["backend_data"] = True - if _should_exclude_job_submission_jrd_cpu_arch(latest_job_submission): - latest_job_submission_jrd_offer_excludes["instance"] = {"resources": {"cpu_arch"}} - if latest_job_submission_jrd_offer_excludes: - latest_job_submission_excludes["job_runtime_data"] = { - "offer": latest_job_submission_jrd_offer_excludes - } - if latest_job_submission.exit_status is None: - latest_job_submission_excludes["exit_status"] = True - if latest_job_submission.status_message == "": - latest_job_submission_excludes["status_message"] = True - if latest_job_submission.error is None: - latest_job_submission_excludes["error"] = True - if latest_job_submission.deployment_num == 0: - latest_job_submission_excludes["deployment_num"] = True - if not latest_job_submission.probes: - latest_job_submission_excludes["probes"] = True return {"plan": apply_plan_excludes} @@ -117,8 +36,6 @@ def get_get_plan_excludes(request: GetRunPlanRequest) -> Optional[IncludeExclude run_spec_excludes = get_run_spec_excludes(request.run_spec) if run_spec_excludes is not None: get_plan_excludes["run_spec"] = run_spec_excludes - if request.max_offers is None: - get_plan_excludes["max_offers"] = True return get_plan_excludes @@ -131,53 +48,13 @@ def get_run_spec_excludes(run_spec: RunSpec) -> IncludeExcludeDictType: spec_excludes: IncludeExcludeDictType = {} configuration_excludes: IncludeExcludeDictType = {} profile_excludes: IncludeExcludeSetType = set() - configuration = run_spec.configuration - profile = run_spec.profile - if not FeatureFlags.LEGACY_REPO_DIR_DISABLED: - if run_spec.repo_dir in [None, LEGACY_REPO_DIR]: - spec_excludes["repo_dir"] = True - elif run_spec.repo_dir == "." and configuration.working_dir in [ - None, - LEGACY_REPO_DIR, - ".", - ]: - spec_excludes["repo_dir"] = True - - if configuration.fleets is None: - configuration_excludes["fleets"] = True - if profile is not None and profile.fleets is None: - profile_excludes.add("fleets") - if configuration.tags is None: - configuration_excludes["tags"] = True - if profile is not None and profile.tags is None: - profile_excludes.add("tags") - if isinstance(configuration, ServiceConfiguration) and not configuration.rate_limits: - configuration_excludes["rate_limits"] = True - if configuration.shell is None: - configuration_excludes["shell"] = True - if configuration.docker is None: - configuration_excludes["docker"] = True - if configuration.priority is None: - configuration_excludes["priority"] = True - if configuration.startup_order is None: - configuration_excludes["startup_order"] = True - if profile is not None and profile.startup_order is None: - profile_excludes.add("startup_order") - if configuration.stop_criteria is None: - configuration_excludes["stop_criteria"] = True - if isinstance(configuration, ServiceConfiguration) and not configuration.probes: - configuration_excludes["probes"] = True - if profile is not None and profile.stop_criteria is None: - profile_excludes.add("stop_criteria") - if not configuration.files: - configuration_excludes["files"] = True - if not run_spec.file_archives: - spec_excludes["file_archives"] = True - if configuration.schedule is None: - configuration_excludes["schedule"] = True - if profile is not None and profile.schedule is None: - profile_excludes.add("schedule") + # Add excludes like this: + # + # if run_spec.configuration.tags is None: + # configuration_excludes["tags"] = True + # if run_spec.profile is not None and run_spec.profile.tags is None: + # profile_excludes.add("tags") if configuration_excludes: spec_excludes["configuration"] = configuration_excludes @@ -193,34 +70,4 @@ def get_job_spec_excludes(job_specs: list[JobSpec]) -> IncludeExcludeDictType: clients backward-compatibility with older servers. """ spec_excludes: IncludeExcludeDictType = {} - - if all(s.repo_code_hash is None for s in job_specs): - spec_excludes["repo_code_hash"] = True - if all(s.repo_data is None for s in job_specs): - spec_excludes["repo_data"] = True - if all(not s.file_archives for s in job_specs): - spec_excludes["file_archives"] = True - if all(s.service_port is None for s in job_specs): - spec_excludes["service_port"] = True - if all(not s.probes for s in job_specs): - spec_excludes["probes"] = True - if all(s.repo_dir in [None, LEGACY_REPO_DIR] for s in job_specs): - spec_excludes["repo_dir"] = True - if all(s.requirements.multinode is None for s in job_specs): - spec_excludes["requirements"] = {"multinode": True} - return spec_excludes - - -def _should_exclude_job_submission_jpd_cpu_arch(job_submission: JobSubmission) -> bool: - try: - return job_submission.job_provisioning_data.instance_type.resources.cpu_arch is None - except AttributeError: - return True - - -def _should_exclude_job_submission_jrd_cpu_arch(job_submission: JobSubmission) -> bool: - try: - return job_submission.job_runtime_data.offer.instance.resources.cpu_arch is None - except AttributeError: - return True diff --git a/src/dstack/_internal/core/compatibility/volumes.py b/src/dstack/_internal/core/compatibility/volumes.py index 4b7be6bb00..191005e130 100644 --- a/src/dstack/_internal/core/compatibility/volumes.py +++ b/src/dstack/_internal/core/compatibility/volumes.py @@ -28,8 +28,10 @@ def _get_volume_configuration_excludes( configuration: VolumeConfiguration, ) -> IncludeExcludeDictType: configuration_excludes: IncludeExcludeDictType = {} - if configuration.tags is None: - configuration_excludes["tags"] = True - if configuration.auto_cleanup_duration is None: - configuration_excludes["auto_cleanup_duration"] = True + + # Add excludes like this: + # + # if configuration.tags is None: + # configuration_excludes["tags"] = True + return configuration_excludes diff --git a/src/dstack/_internal/core/models/fleets.py b/src/dstack/_internal/core/models/fleets.py index 1137c38dba..c88f606640 100644 --- a/src/dstack/_internal/core/models/fleets.py +++ b/src/dstack/_internal/core/models/fleets.py @@ -21,7 +21,6 @@ ProfileParams, ProfileRetry, SpotPolicy, - TerminationPolicy, parse_idle_duration, ) from dstack._internal.core.models.resources import ResourcesSpec @@ -210,8 +209,6 @@ def _post_validate_ranges(cls, values): class InstanceGroupParamsConfig(CoreConfig): @staticmethod def schema_extra(schema: Dict[str, Any]): - del schema["properties"]["termination_policy"] - del schema["properties"]["termination_idle_time"] add_extra_schema_types( schema["properties"]["nodes"], extra_types=[{"type": "integer"}, {"type": "string"}], @@ -318,10 +315,6 @@ class InstanceGroupParams(CoreModel): ), ] = None - # Deprecated and unused. Left for compatibility with 0.18 clients. - termination_policy: Annotated[Optional[TerminationPolicy], Field(exclude=True)] = None - termination_idle_time: Annotated[Optional[Union[str, int]], Field(exclude=True)] = None - @validator("nodes", pre=True) def parse_nodes(cls, v: Optional[Union[dict, str]]) -> Optional[dict]: if isinstance(v, str) and ".." in v: diff --git a/src/dstack/_internal/core/models/gateways.py b/src/dstack/_internal/core/models/gateways.py index 39befe7392..ace68e5429 100644 --- a/src/dstack/_internal/core/models/gateways.py +++ b/src/dstack/_internal/core/models/gateways.py @@ -96,10 +96,11 @@ class Gateway(CoreModel): instance_id: Optional[str] wildcard_domain: Optional[str] default: bool - # TODO: configuration fields are duplicated on top-level for backward compatibility with 0.18.x - # Remove after 0.19 - backend: BackendType - region: str + # TODO: Deprecated configuration fields duplicated on top-level + # for backward compatibility with 0.19.x clients that expect them required. + # Remove after 0.21 + backend: Optional[BackendType] = None + region: Optional[str] = None class GatewayPlan(CoreModel): diff --git a/src/dstack/_internal/core/models/profiles.py b/src/dstack/_internal/core/models/profiles.py index 7fae65e762..896049ffa6 100644 --- a/src/dstack/_internal/core/models/profiles.py +++ b/src/dstack/_internal/core/models/profiles.py @@ -242,11 +242,6 @@ def crons(self) -> List[str]: class ProfileParamsConfig(CoreConfig): @staticmethod def schema_extra(schema: Dict[str, Any]): - del schema["properties"]["pool_name"] - del schema["properties"]["instance_name"] - del schema["properties"]["retry_policy"] - del schema["properties"]["termination_policy"] - del schema["properties"]["termination_idle_time"] add_extra_schema_types( schema["properties"]["max_duration"], extra_types=[{"type": "boolean"}, {"type": "string"}], @@ -396,13 +391,6 @@ class ProfileParams(CoreModel): ), ] = None - # Deprecated and unused. Left for compatibility with 0.18 clients. - pool_name: Annotated[Optional[str], Field(exclude=True)] = None - instance_name: Annotated[Optional[str], Field(exclude=True)] = None - retry_policy: Annotated[Optional[ProfileRetryPolicy], Field(exclude=True)] = None - termination_policy: Annotated[Optional[TerminationPolicy], Field(exclude=True)] = None - termination_idle_time: Annotated[Optional[Union[str, int]], Field(exclude=True)] = None - _validate_max_duration = validator("max_duration", pre=True, allow_reuse=True)( parse_max_duration ) diff --git a/src/dstack/_internal/core/models/repos/remote.py b/src/dstack/_internal/core/models/repos/remote.py index 8d6d371952..fedadee0ff 100644 --- a/src/dstack/_internal/core/models/repos/remote.py +++ b/src/dstack/_internal/core/models/repos/remote.py @@ -3,7 +3,7 @@ import subprocess import time from dataclasses import dataclass -from typing import Annotated, Any, BinaryIO, Callable, Dict, Optional +from typing import Any, BinaryIO, Callable, Dict, Optional import git import pydantic @@ -27,7 +27,7 @@ class RepoError(DstackError): class RemoteRepoCredsConfig(CoreConfig): @staticmethod def schema_extra(schema: Dict[str, Any]): - del schema["properties"]["protocol"] + pass class RemoteRepoCreds(generate_dual_core_model(RemoteRepoCredsConfig)): @@ -35,16 +35,11 @@ class RemoteRepoCreds(generate_dual_core_model(RemoteRepoCredsConfig)): private_key: Optional[str] = None oauth_token: Optional[str] = None - # TODO: remove in 0.20. Left for compatibility with CLI <=0.18.44 - protocol: Annotated[Optional[str], Field(exclude=True)] = None - class RemoteRepoInfoConfig(CoreConfig): @staticmethod def schema_extra(schema: Dict[str, Any]): - del schema["properties"]["repo_host_name"] - del schema["properties"]["repo_port"] - del schema["properties"]["repo_user_name"] + pass class RemoteRepoInfo( @@ -54,11 +49,6 @@ class RemoteRepoInfo( repo_type: Literal["remote"] = "remote" repo_name: str - # TODO: remove in 0.20. Left for compatibility with CLI <=0.18.44 - repo_host_name: Annotated[Optional[str], Field(exclude=True)] = None - repo_port: Annotated[Optional[int], Field(exclude=True)] = None - repo_user_name: Annotated[Optional[str], Field(exclude=True)] = None - class RemoteRunRepoData(RemoteRepoInfo): repo_branch: Optional[str] = None diff --git a/src/dstack/_internal/core/models/runs.py b/src/dstack/_internal/core/models/runs.py index ec657e7987..841d58bd66 100644 --- a/src/dstack/_internal/core/models/runs.py +++ b/src/dstack/_internal/core/models/runs.py @@ -203,7 +203,6 @@ def to_error(self) -> Optional[str]: class Requirements(CoreModel): - # TODO: Make requirements' fields required resources: ResourcesSpec max_price: Optional[float] = None spot: Optional[bool] = None diff --git a/src/dstack/_internal/server/background/tasks/process_running_jobs.py b/src/dstack/_internal/server/background/tasks/process_running_jobs.py index 72c5417c12..ba2d96a135 100644 --- a/src/dstack/_internal/server/background/tasks/process_running_jobs.py +++ b/src/dstack/_internal/server/background/tasks/process_running_jobs.py @@ -386,8 +386,7 @@ async def _process_running_job(session: AsyncSession, job_model: JobModel): fmt(job_model), job_submission.age, ) - # TODO: Replace with JobTerminationReason.INSTANCE_UNREACHABLE in 0.20 or - # when CLI <= 0.19.8 is no longer supported + # TODO: Replace with JobTerminationReason.INSTANCE_UNREACHABLE for on-demand. job_model.termination_reason = JobTerminationReason.INTERRUPTED_BY_NO_CAPACITY job_model.status = JobStatus.TERMINATING else: diff --git a/src/dstack/_internal/server/models.py b/src/dstack/_internal/server/models.py index 04185762a1..374b359edb 100644 --- a/src/dstack/_internal/server/models.py +++ b/src/dstack/_internal/server/models.py @@ -637,7 +637,7 @@ class InstanceModel(BaseModel): termination_deadline: Mapped[Optional[datetime]] = mapped_column(NaiveDateTime) termination_reason: Mapped[Optional[str]] = mapped_column(String(4000)) # Deprecated since 0.19.22, not used - health_status: Mapped[Optional[str]] = mapped_column(String(4000)) + health_status: Mapped[Optional[str]] = mapped_column(String(4000), deferred=True) health: Mapped[HealthStatus] = mapped_column( EnumAsString(HealthStatus, 100), default=HealthStatus.HEALTHY ) diff --git a/src/dstack/_internal/server/routers/fleets.py b/src/dstack/_internal/server/routers/fleets.py index 3cbab9508c..7e7126f4bf 100644 --- a/src/dstack/_internal/server/routers/fleets.py +++ b/src/dstack/_internal/server/routers/fleets.py @@ -138,7 +138,7 @@ async def apply_plan( ) -@project_router.post("/create", response_model=Fleet) +@project_router.post("/create", response_model=Fleet, deprecated=True) async def create_fleet( body: CreateFleetRequest, session: AsyncSession = Depends(get_session), diff --git a/src/dstack/_internal/server/schemas/gateways.py b/src/dstack/_internal/server/schemas/gateways.py index ffad6e78ab..9c00caa2e3 100644 --- a/src/dstack/_internal/server/schemas/gateways.py +++ b/src/dstack/_internal/server/schemas/gateways.py @@ -1,8 +1,5 @@ -from typing import Annotated, Any, Dict, List, Optional +from typing import Any, Dict, List -from pydantic import Field - -from dstack._internal.core.models.backends.base import BackendType from dstack._internal.core.models.common import CoreConfig, CoreModel, generate_dual_core_model from dstack._internal.core.models.gateways import GatewayConfiguration @@ -10,17 +7,11 @@ class CreateGatewayRequestConfig(CoreConfig): @staticmethod def schema_extra(schema: Dict[str, Any]): - del schema["properties"]["name"] - del schema["properties"]["backend_type"] - del schema["properties"]["region"] + pass class CreateGatewayRequest(generate_dual_core_model(CreateGatewayRequestConfig)): configuration: GatewayConfiguration - # Deprecated and unused. Left for compatibility with 0.18 clients. - name: Annotated[Optional[str], Field(exclude=True)] = None - backend_type: Annotated[Optional[BackendType], Field(exclude=True)] = None - region: Annotated[Optional[str], Field(exclude=True)] = None class GetGatewayRequest(CoreModel): diff --git a/src/dstack/api/_public/runs.py b/src/dstack/api/_public/runs.py index f7d3a0170d..6a4843b023 100644 --- a/src/dstack/api/_public/runs.py +++ b/src/dstack/api/_public/runs.py @@ -825,7 +825,7 @@ def list(self, all: bool = False, limit: Optional[int] = None) -> List[Run]: repo_id=None, only_active=only_active, limit=limit or 100, - # TODO: Pass job_submissions_limit=1 in 0.20 + job_submissions_limit=1, ) if only_active and len(runs) == 0: runs = self._api_client.runs.list( diff --git a/src/dstack/api/server/_fleets.py b/src/dstack/api/server/_fleets.py index 942f12c27a..8f6ea7fcfa 100644 --- a/src/dstack/api/server/_fleets.py +++ b/src/dstack/api/server/_fleets.py @@ -63,7 +63,7 @@ def delete_instances(self, project_name: str, name: str, instance_nums: List[int self._request(f"/api/project/{project_name}/fleets/delete_instances", body=body.json()) # Deprecated - # TODO: Remove in 0.20 + # TODO: Remove in 0.21 def create( self, project_name: str, diff --git a/src/tests/_internal/server/background/tasks/test_process_runs.py b/src/tests/_internal/server/background/tasks/test_process_runs.py index ca1ac060ef..81c1ef0026 100644 --- a/src/tests/_internal/server/background/tasks/test_process_runs.py +++ b/src/tests/_internal/server/background/tasks/test_process_runs.py @@ -1015,7 +1015,3 @@ async def test_stops_unregistered_out_of_date_replicas_unconditionally( cast(JobSpec, JobSpec.__response__.parse_raw(run.jobs[2].job_spec_data)).image_name == "new" ) - - -# TODO(egor-s): TestProcessRunsMultiNode -# TODO(egor-s): TestProcessRunsAutoScaling