Skip to content

Commit 9bed338

Browse files
Andrey Cheptsovclaude
andcommitted
Move log_quota_hour from JobSpec to SubmitBody for backward compatibility
JobSpec is sent client→server as part of RunPlan.current_resource, so new fields break older servers. log_quota_hour is only needed server→runner, so it belongs on SubmitBody instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6247739 commit 9bed338

8 files changed

Lines changed: 9 additions & 17 deletions

File tree

runner/internal/runner/executor/executor.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,12 +262,12 @@ func (ex *RunExecutor) Run(ctx context.Context) (err error) {
262262
}
263263

264264
if errors.Is(err, ErrLogQuotaExceeded) {
265-
log.Error(ctx, "Log quota exceeded", "quota", ex.jobSpec.LogQuotaHour)
265+
log.Error(ctx, "Log quota exceeded", "quota", ex.jobLogs.quota)
266266
ex.SetJobStateWithTerminationReason(
267267
ctx,
268268
schemas.JobStateFailed,
269269
types.TerminationReasonLogQuotaExceeded,
270-
fmt.Sprintf("Job log output exceeded the hourly quota of %d bytes", ex.jobSpec.LogQuotaHour),
270+
fmt.Sprintf("Job log output exceeded the hourly quota of %d bytes", ex.jobLogs.quota),
271271
)
272272
return fmt.Errorf("log quota exceeded: %w", err)
273273
}
@@ -294,7 +294,7 @@ func (ex *RunExecutor) SetJob(body schemas.SubmitBody) {
294294
ex.clusterInfo = body.ClusterInfo
295295
ex.secrets = body.Secrets
296296
ex.repoCredentials = body.RepoCredentials
297-
ex.jobLogs.SetQuota(body.JobSpec.LogQuotaHour)
297+
ex.jobLogs.SetQuota(body.LogQuotaHour)
298298
ex.state = WaitCode
299299
}
300300

runner/internal/runner/executor/executor_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ func TestExecutor_LogQuota(t *testing.T) {
150150
ex.killDelay = 500 * time.Millisecond
151151
// Output >100 bytes to trigger the quota
152152
ex.jobSpec.Commands = append(ex.jobSpec.Commands, "for i in $(seq 1 20); do echo 'This line is long enough to exceed the quota easily'; done")
153-
ex.jobSpec.LogQuotaHour = 100 // 100 bytes
154153
ex.jobLogs.SetQuota(100)
155154
makeCodeTar(t, ex)
156155

runner/internal/runner/schemas/schemas.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type SubmitBody struct {
3636
ClusterInfo ClusterInfo `json:"cluster_info"`
3737
Secrets map[string]string `json:"secrets"`
3838
RepoCredentials *RepoCredentials `json:"repo_credentials"`
39+
LogQuotaHour int `json:"log_quota_hour"` // bytes per hour, 0 = unlimited
3940
}
4041

4142
type PullResponse struct {
@@ -79,7 +80,6 @@ type JobSpec struct {
7980
Env map[string]string `json:"env"`
8081
SingleBranch bool `json:"single_branch"`
8182
MaxDuration int `json:"max_duration"`
82-
LogQuotaHour int `json:"log_quota_hour"` // bytes per hour, 0 = unlimited
8383
SSHKey *SSHKey `json:"ssh_key"`
8484
WorkingDir *string `json:"working_dir"`
8585
RepoDir *string `json:"repo_dir"`

src/dstack/_internal/core/models/runs.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,6 @@ class JobSpec(CoreModel):
278278
privileged: bool = False
279279
single_branch: Optional[bool] = None
280280
max_duration: Optional[int]
281-
log_quota_hour: Optional[int] = None
282-
"""`log_quota_hour` is the maximum number of bytes of log output per calendar hour.
283-
`None` means unlimited. Set from `DSTACK_SERVER_LOG_QUOTA_PER_JOB_HOUR`."""
284281
stop_duration: Optional[int] = None
285282
utilization_policy: Optional[UtilizationPolicy] = None
286283
registry_auth: Optional[RegistryAuth]

src/dstack/_internal/server/schemas/runner.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ class SubmitBody(CoreModel):
8383
"gateway",
8484
"single_branch",
8585
"max_duration",
86-
"log_quota_hour",
8786
"ssh_key",
8887
"working_dir",
8988
"repo_dir",
@@ -104,6 +103,8 @@ class SubmitBody(CoreModel):
104103
cluster_info: Annotated[Optional[ClusterInfo], Field(include=True)]
105104
secrets: Annotated[Optional[Dict[str, str]], Field(include=True)]
106105
repo_credentials: Annotated[Optional[RemoteRepoCreds], Field(include=True)]
106+
log_quota_hour: Optional[int] = None
107+
"""Maximum bytes of log output per hour. None means unlimited."""
107108
# TODO: remove `run_spec` once instances deployed with 0.19.8 or earlier are no longer supported.
108109
run_spec: Annotated[
109110
RunSpec,

src/dstack/_internal/server/services/jobs/configurators/base.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@
4848
from dstack._internal.core.models.volumes import MountPoint, VolumeMountPoint
4949
from dstack._internal.core.services.profiles import get_retry
5050
from dstack._internal.core.services.ssh.ports import filter_reserved_ports
51-
from dstack._internal.server import settings as server_settings
5251
from dstack._internal.server.services.docker import ImageConfig, get_image_config
5352
from dstack._internal.utils import crypto
5453
from dstack._internal.utils.common import run_async
@@ -170,7 +169,6 @@ async def _get_job_spec(
170169
privileged=self._privileged(),
171170
single_branch=self._single_branch(),
172171
max_duration=self._max_duration(),
173-
log_quota_hour=self._log_quota_hour(),
174172
stop_duration=self._stop_duration(),
175173
utilization_policy=self._utilization_policy(),
176174
registry_auth=self._registry_auth(),
@@ -306,10 +304,6 @@ def _max_duration(self) -> Optional[int]:
306304
return None
307305
return self.run_spec.merged_profile.max_duration
308306

309-
def _log_quota_hour(self) -> Optional[int]:
310-
quota = server_settings.SERVER_LOG_QUOTA_PER_JOB_HOUR
311-
return quota if quota > 0 else None
312-
313307
def _stop_duration(self) -> Optional[int]:
314308
if self.run_spec.merged_profile.stop_duration is None:
315309
return DEFAULT_STOP_DURATION

src/dstack/_internal/server/services/runner/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from dstack._internal.core.models.resources import Memory
1616
from dstack._internal.core.models.runs import ClusterInfo, Job, Run
1717
from dstack._internal.core.models.volumes import InstanceMountPoint, Volume, VolumeMountPoint
18+
from dstack._internal.server import settings as server_settings
1819
from dstack._internal.server.schemas.instances import InstanceCheck
1920
from dstack._internal.server.schemas.runner import (
2021
ComponentInfo,
@@ -93,13 +94,15 @@ def submit_job(
9394
merged_env.update(job_spec.env)
9495
job_spec = job_spec.copy(deep=True)
9596
job_spec.env = merged_env
97+
quota = server_settings.SERVER_LOG_QUOTA_PER_JOB_HOUR
9698
body = SubmitBody(
9799
run=run,
98100
job_spec=job_spec,
99101
job_submission=job.job_submissions[-1],
100102
cluster_info=cluster_info,
101103
secrets=secrets,
102104
repo_credentials=repo_credentials,
105+
log_quota_hour=quota if quota > 0 else None,
103106
run_spec=run.run_spec,
104107
)
105108
resp = requests.post(

src/tests/_internal/server/routers/test_runs.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,6 @@ def get_dev_env_run_plan_dict(
269269
"replica_group": "0",
270270
"single_branch": False,
271271
"max_duration": None,
272-
"log_quota_hour": 52428800,
273272
"stop_duration": 300,
274273
"utilization_policy": None,
275274
"registry_auth": None,
@@ -507,7 +506,6 @@ def get_dev_env_run_dict(
507506
"replica_group": "0",
508507
"single_branch": False,
509508
"max_duration": None,
510-
"log_quota_hour": 52428800,
511509
"stop_duration": 300,
512510
"utilization_policy": None,
513511
"registry_auth": None,

0 commit comments

Comments
 (0)