Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbe0e6f3

Browse files
committed
feat: implement autostop database/http/api plumbing
1 parent4a7e6eb commitbe0e6f3

File tree

4 files changed

+183
-4
lines changed

4 files changed

+183
-4
lines changed

‎coderd/coderd.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,9 @@ func New(options *Options) (http.Handler, func()) {
187187
r.Route("/autostart",func(r chi.Router) {
188188
r.Put("/",api.putWorkspaceAutostart)
189189
})
190+
r.Route("/autostop",func(r chi.Router) {
191+
r.Put("/",api.putWorkspaceAutostop)
192+
})
190193
})
191194
r.Route("/workspacebuilds/{workspacebuild}",func(r chi.Router) {
192195
r.Use(

‎coderd/workspaces.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,37 @@ func (api *api) putWorkspaceAutostart(rw http.ResponseWriter, r *http.Request) {
323323
}
324324
}
325325

326-
// TODO(cian): api.updateWorkspaceAutostop
326+
func (api*api)putWorkspaceAutostop(rw http.ResponseWriter,r*http.Request) {
327+
varreq codersdk.UpdateWorkspaceAutostopRequest
328+
if!httpapi.Read(rw,r,&req) {
329+
return
330+
}
331+
332+
vardbSched sql.NullString
333+
ifreq.Schedule!="" {
334+
validSched,err:=schedule.Weekly(req.Schedule)
335+
iferr!=nil {
336+
httpapi.Write(rw,http.StatusInternalServerError, httpapi.Response{
337+
Message:fmt.Sprintf("invalid autostop schedule: %s",err),
338+
})
339+
return
340+
}
341+
dbSched.String=validSched.String()
342+
dbSched.Valid=true
343+
}
344+
345+
workspace:=httpmw.WorkspaceParam(r)
346+
err:=api.Database.UpdateWorkspaceAutostop(r.Context(), database.UpdateWorkspaceAutostopParams{
347+
ID:workspace.ID,
348+
AutostopSchedule:dbSched,
349+
})
350+
iferr!=nil {
351+
httpapi.Write(rw,http.StatusInternalServerError, httpapi.Response{
352+
Message:fmt.Sprintf("update workspace autostop schedule: %s",err),
353+
})
354+
return
355+
}
356+
}
327357

328358
funcconvertWorkspace(workspace database.Workspace,workspaceBuild codersdk.WorkspaceBuild,template database.Template) codersdk.Workspace {
329359
return codersdk.Workspace{

‎coderd/workspaces_test.go

Lines changed: 130 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,136 @@ func TestWorkspaceUpdateAutostart(t *testing.T) {
309309

310310
err:=client.UpdateWorkspaceAutostart(ctx,wsid,req)
311311
require.IsType(t,err,&codersdk.Error{},"expected codersdk.Error")
312-
coderSDKErr,_:=err.(*codersdk.Error)
312+
coderSDKErr,_:=err.(*codersdk.Error)//nolint:errorlint
313+
require.Equal(t,coderSDKErr.StatusCode(),404,"expected status code 404")
314+
require.Equal(t,fmt.Sprintf("workspace %q does not exist",wsid),coderSDKErr.Message,"unexpected response code")
315+
})
316+
}
317+
318+
funcTestWorkspaceUpdateAutostop(t*testing.T) {
319+
t.Parallel()
320+
vardublinLoc=mustLocation(t,"Europe/Dublin")
321+
322+
testCases:= []struct {
323+
namestring
324+
schedulestring
325+
expectedErrorstring
326+
at time.Time
327+
expectedNext time.Time
328+
expectedInterval time.Duration
329+
}{
330+
{
331+
name:"disable autostop",
332+
schedule:"",
333+
expectedError:"",
334+
},
335+
{
336+
name:"friday to monday",
337+
schedule:"CRON_TZ=Europe/Dublin 30 17 1-5",
338+
expectedError:"",
339+
at:time.Date(2022,5,6,17,31,0,0,dublinLoc),
340+
expectedNext:time.Date(2022,5,9,17,30,0,0,dublinLoc),
341+
expectedInterval:71*time.Hour+59*time.Minute,
342+
},
343+
{
344+
name:"monday to tuesday",
345+
schedule:"CRON_TZ=Europe/Dublin 30 17 1-5",
346+
expectedError:"",
347+
at:time.Date(2022,5,9,17,31,0,0,dublinLoc),
348+
expectedNext:time.Date(2022,5,10,17,30,0,0,dublinLoc),
349+
expectedInterval:23*time.Hour+59*time.Minute,
350+
},
351+
{
352+
// DST in Ireland began on Mar 27 in 2022 at 0100. Forward 1 hour.
353+
name:"DST start",
354+
schedule:"CRON_TZ=Europe/Dublin 30 17 *",
355+
expectedError:"",
356+
at:time.Date(2022,3,26,17,31,0,0,dublinLoc),
357+
expectedNext:time.Date(2022,3,27,17,30,0,0,dublinLoc),
358+
expectedInterval:22*time.Hour+59*time.Minute,
359+
},
360+
{
361+
// DST in Ireland ends on Oct 30 in 2022 at 0200. Back 1 hour.
362+
name:"DST end",
363+
schedule:"CRON_TZ=Europe/Dublin 30 17 *",
364+
expectedError:"",
365+
at:time.Date(2022,10,29,17,31,0,0,dublinLoc),
366+
expectedNext:time.Date(2022,10,30,17,30,0,0,dublinLoc),
367+
expectedInterval:24*time.Hour+59*time.Minute,
368+
},
369+
{
370+
name:"invalid location",
371+
schedule:"CRON_TZ=Imaginary/Place 30 17 1-5",
372+
expectedError:"status code 500: invalid autostop schedule: parse schedule: provided bad location Imaginary/Place: unknown time zone Imaginary/Place",
373+
},
374+
{
375+
name:"invalid schedule",
376+
schedule:"asdf asdf asdf ",
377+
expectedError:`status code 500: invalid autostop schedule: parse schedule: failed to parse int from asdf: strconv.Atoi: parsing "asdf": invalid syntax`,
378+
},
379+
}
380+
381+
for_,testCase:=rangetestCases {
382+
testCase:=testCase
383+
t.Run(testCase.name,func(t*testing.T) {
384+
t.Parallel()
385+
var (
386+
ctx=context.Background()
387+
client=coderdtest.New(t,nil)
388+
_=coderdtest.NewProvisionerDaemon(t,client)
389+
user=coderdtest.CreateFirstUser(t,client)
390+
version=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,nil)
391+
_=coderdtest.AwaitTemplateVersionJob(t,client,version.ID)
392+
project=coderdtest.CreateTemplate(t,client,user.OrganizationID,version.ID)
393+
workspace=coderdtest.CreateWorkspace(t,client,codersdk.Me,project.ID)
394+
)
395+
396+
// ensure test invariant: new workspaces have no autostop schedule.
397+
require.Empty(t,workspace.AutostopSchedule,"expected newly-minted workspace to have no autstop schedule")
398+
399+
err:=client.UpdateWorkspaceAutostop(ctx,workspace.ID, codersdk.UpdateWorkspaceAutostopRequest{
400+
Schedule:testCase.schedule,
401+
})
402+
403+
iftestCase.expectedError!="" {
404+
require.EqualError(t,err,testCase.expectedError,"unexpected error when setting workspace autostop schedule")
405+
return
406+
}
407+
408+
require.NoError(t,err,"expected no error setting workspace autostop schedule")
409+
410+
updated,err:=client.Workspace(ctx,workspace.ID)
411+
require.NoError(t,err,"fetch updated workspace")
412+
413+
require.Equal(t,testCase.schedule,updated.AutostopSchedule,"expected autostop schedule to equal requested")
414+
415+
iftestCase.schedule=="" {
416+
return
417+
}
418+
sched,err:=schedule.Weekly(updated.AutostopSchedule)
419+
require.NoError(t,err,"parse returned schedule")
420+
421+
next:=sched.Next(testCase.at)
422+
require.Equal(t,testCase.expectedNext,next,"unexpected next scheduled autostop time")
423+
interval:=next.Sub(testCase.at)
424+
require.Equal(t,testCase.expectedInterval,interval,"unexpected interval")
425+
})
426+
}
427+
428+
t.Run("NotFound",func(t*testing.T) {
429+
var (
430+
ctx=context.Background()
431+
client=coderdtest.New(t,nil)
432+
_=coderdtest.CreateFirstUser(t,client)
433+
wsid=uuid.New()
434+
req= codersdk.UpdateWorkspaceAutostopRequest{
435+
Schedule:"9 30 1-5",
436+
}
437+
)
438+
439+
err:=client.UpdateWorkspaceAutostop(ctx,wsid,req)
440+
require.IsType(t,err,&codersdk.Error{},"expected codersdk.Error")
441+
coderSDKErr,_:=err.(*codersdk.Error)//nolint:errorlint
313442
require.Equal(t,coderSDKErr.StatusCode(),404,"expected status code 404")
314443
require.Equal(t,fmt.Sprintf("workspace %q does not exist",wsid),coderSDKErr.Message,"unexpected response code")
315444
})

‎codersdk/workspaces.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,25 @@ func (c *Client) UpdateWorkspaceAutostart(ctx context.Context, id uuid.UUID, req
107107
ifres.StatusCode!=http.StatusOK {
108108
returnreadBodyAsError(res)
109109
}
110-
// TODO(cian): should we return the updated schedule?
111110
returnnil
112111
}
113112

114-
// TODO(cian): client.UpdateWorkspaceAutostop
113+
// UpdateWorkspaceAutostopRequest is a request to update a workspace's autostop schedule.
114+
typeUpdateWorkspaceAutostopRequeststruct {
115+
Schedulestring
116+
}
117+
118+
// UpdateWorkspaceAutostop sets the autostop schedule for workspace by id.
119+
// If the provided schedule is empty, autostop is disabled for the workspace.
120+
func (c*Client)UpdateWorkspaceAutostop(ctx context.Context,id uuid.UUID,reqUpdateWorkspaceAutostopRequest)error {
121+
path:=fmt.Sprintf("/api/v2/workspaces/%s/autostop",id.String())
122+
res,err:=c.request(ctx,http.MethodPut,path,req)
123+
iferr!=nil {
124+
returnxerrors.Errorf("update workspace autostop: %w",err)
125+
}
126+
deferres.Body.Close()
127+
ifres.StatusCode!=http.StatusOK {
128+
returnreadBodyAsError(res)
129+
}
130+
returnnil
131+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp