@Summary Update workspace TTL by ID @ID update-workspace-ttl-by-id @Security CoderSessionToken @Accept json @Tags Workspaces @Param workspace path string true "Workspace ID" format(uuid) @Param request body codersdk.UpdateWorkspaceTTLRequest true "Workspace TTL update request" @Success 204 @Router /
(rw http.ResponseWriter, r *http.Request)
| 1313 | // @Success 204 |
| 1314 | // @Router /api/v2/workspaces/{workspace}/ttl [put] |
| 1315 | func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { |
| 1316 | var ( |
| 1317 | ctx = r.Context() |
| 1318 | workspace = httpmw.WorkspaceParam(r) |
| 1319 | auditor = api.Auditor.Load() |
| 1320 | aReq, commitAudit = audit.InitRequest[database.WorkspaceTable](rw, &audit.RequestParams{ |
| 1321 | Audit: *auditor, |
| 1322 | Log: api.Logger, |
| 1323 | Request: r, |
| 1324 | Action: database.AuditActionWrite, |
| 1325 | OrganizationID: workspace.OrganizationID, |
| 1326 | }) |
| 1327 | ) |
| 1328 | defer commitAudit() |
| 1329 | aReq.Old = workspace.WorkspaceTable() |
| 1330 | |
| 1331 | var req codersdk.UpdateWorkspaceTTLRequest |
| 1332 | if !httpapi.Read(ctx, rw, r, &req) { |
| 1333 | return |
| 1334 | } |
| 1335 | |
| 1336 | // TTL updates are not supported for prebuilt workspaces. |
| 1337 | // Prebuild lifecycle is managed by the reconciliation loop, with TTL behavior |
| 1338 | // defined per preset at the template level, not per workspace. |
| 1339 | if workspace.IsPrebuild() { |
| 1340 | httpapi.Write(ctx, rw, http.StatusConflict, codersdk.Response{ |
| 1341 | Message: "TTL updates are not supported for prebuilt workspaces", |
| 1342 | Detail: "Prebuilt workspace TTL is configured per preset at the template level. Workspace-level overrides are not supported.", |
| 1343 | }) |
| 1344 | return |
| 1345 | } |
| 1346 | |
| 1347 | var dbTTL sql.NullInt64 |
| 1348 | |
| 1349 | err := api.Database.InTx(func(s database.Store) error { |
| 1350 | templateSchedule, err := (*api.TemplateScheduleStore.Load()).Get(ctx, s, workspace.TemplateID) |
| 1351 | if err != nil { |
| 1352 | return xerrors.Errorf("get template schedule: %w", err) |
| 1353 | } |
| 1354 | if !templateSchedule.UserAutostopEnabled { |
| 1355 | return codersdk.ValidationError{Field: "ttl_ms", Detail: "Custom autostop TTL is not allowed for workspaces using this template."} |
| 1356 | } |
| 1357 | |
| 1358 | // don't override 0 ttl with template default here because it indicates |
| 1359 | // disabled autostop |
| 1360 | var validityErr error |
| 1361 | dbTTL, validityErr = validWorkspaceTTLMillis(req.TTLMillis, 0) |
| 1362 | if validityErr != nil { |
| 1363 | return codersdk.ValidationError{Field: "ttl_ms", Detail: validityErr.Error()} |
| 1364 | } |
| 1365 | if err := s.UpdateWorkspaceTTL(ctx, database.UpdateWorkspaceTTLParams{ |
| 1366 | ID: workspace.ID, |
| 1367 | Ttl: dbTTL, |
| 1368 | }); err != nil { |
| 1369 | return xerrors.Errorf("update workspace time until shutdown: %w", err) |
| 1370 | } |
| 1371 | |
| 1372 | // Use injected Clock to allow time mocking in tests |
nothing calls this directly
no test coverage detected