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

Commita18eb9d

Browse files
authored
feat(site): allow recreating devcontainers and showing dirty status (#18049)
This change allows showing the devcontainer dirty status in the UI aswell as a recreate button to update the devcontainer.Closes#16424
1 parent23d1423 commita18eb9d

File tree

14 files changed

+275
-46
lines changed

14 files changed

+275
-46
lines changed

‎agent/agent_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2226,7 +2226,7 @@ func TestAgent_DevcontainerRecreate(t *testing.T) {
22262226
// devcontainer, we do it in a goroutine so we can process logs
22272227
// concurrently.
22282228
gofunc(container codersdk.WorkspaceAgentContainer) {
2229-
err:=conn.RecreateDevcontainer(ctx,container.ID)
2229+
_,err:=conn.RecreateDevcontainer(ctx,container.ID)
22302230
assert.NoError(t,err,"recreate devcontainer should succeed")
22312231
}(container)
22322232

‎agent/agentcontainers/api.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code
403403
// Check if the container is running and update the known devcontainers.
404404
fori:=rangeupdated.Containers {
405405
container:=&updated.Containers[i]// Grab a reference to the container to allow mutating it.
406+
container.DevcontainerStatus=""// Reset the status for the container (updated later).
406407
container.DevcontainerDirty=false// Reset dirty state for the container (updated later).
407408

408409
workspaceFolder:=container.Labels[DevcontainerLocalFolderLabel]
@@ -465,16 +466,25 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code
465466
for_,dc:=rangeapi.knownDevcontainers {
466467
switch {
467468
casedc.Status==codersdk.WorkspaceAgentDevcontainerStatusStarting:
469+
ifdc.Container!=nil {
470+
dc.Container.DevcontainerStatus=dc.Status
471+
dc.Container.DevcontainerDirty=dc.Dirty
472+
}
468473
continue// This state is handled by the recreation routine.
469474

470475
casedc.Status==codersdk.WorkspaceAgentDevcontainerStatusError&& (dc.Container==nil||dc.Container.CreatedAt.Before(api.recreateErrorTimes[dc.WorkspaceFolder])):
476+
ifdc.Container!=nil {
477+
dc.Container.DevcontainerStatus=dc.Status
478+
dc.Container.DevcontainerDirty=dc.Dirty
479+
}
471480
continue// The devcontainer needs to be recreated.
472481

473482
casedc.Container!=nil:
474483
dc.Status=codersdk.WorkspaceAgentDevcontainerStatusStopped
475484
ifdc.Container.Running {
476485
dc.Status=codersdk.WorkspaceAgentDevcontainerStatusRunning
477486
}
487+
dc.Container.DevcontainerStatus=dc.Status
478488

479489
dc.Dirty=false
480490
iflastModified,hasModTime:=api.configFileModifiedTimes[dc.ConfigPath];hasModTime&&dc.Container.CreatedAt.Before(lastModified) {
@@ -608,6 +618,9 @@ func (api *API) handleDevcontainerRecreate(w http.ResponseWriter, r *http.Reques
608618
// Update the status so that we don't try to recreate the
609619
// devcontainer multiple times in parallel.
610620
dc.Status=codersdk.WorkspaceAgentDevcontainerStatusStarting
621+
ifdc.Container!=nil {
622+
dc.Container.DevcontainerStatus=dc.Status
623+
}
611624
api.knownDevcontainers[dc.WorkspaceFolder]=dc
612625
api.recreateWg.Add(1)
613626
goapi.recreateDevcontainer(dc,configPath)
@@ -680,6 +693,9 @@ func (api *API) recreateDevcontainer(dc codersdk.WorkspaceAgentDevcontainer, con
680693
api.mu.Lock()
681694
dc=api.knownDevcontainers[dc.WorkspaceFolder]
682695
dc.Status=codersdk.WorkspaceAgentDevcontainerStatusError
696+
ifdc.Container!=nil {
697+
dc.Container.DevcontainerStatus=dc.Status
698+
}
683699
api.knownDevcontainers[dc.WorkspaceFolder]=dc
684700
api.recreateErrorTimes[dc.WorkspaceFolder]=api.clock.Now("recreate","errorTimes")
685701
api.mu.Unlock()
@@ -695,10 +711,12 @@ func (api *API) recreateDevcontainer(dc codersdk.WorkspaceAgentDevcontainer, con
695711
// allows the update routine to update the devcontainer status, but
696712
// to minimize the time between API consistency, we guess the status
697713
// based on the container state.
698-
ifdc.Container!=nil&&dc.Container.Running {
699-
dc.Status=codersdk.WorkspaceAgentDevcontainerStatusRunning
700-
}else {
701-
dc.Status=codersdk.WorkspaceAgentDevcontainerStatusStopped
714+
dc.Status=codersdk.WorkspaceAgentDevcontainerStatusStopped
715+
ifdc.Container!=nil {
716+
ifdc.Container.Running {
717+
dc.Status=codersdk.WorkspaceAgentDevcontainerStatusRunning
718+
}
719+
dc.Container.DevcontainerStatus=dc.Status
702720
}
703721
dc.Dirty=false
704722
api.recreateSuccessTimes[dc.WorkspaceFolder]=api.clock.Now("recreate","successTimes")

‎agent/agentcontainers/api_test.go

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,8 @@ func TestAPI(t *testing.T) {
477477
require.NoError(t,err,"unmarshal response failed")
478478
require.Len(t,resp.Devcontainers,1,"expected one devcontainer in response")
479479
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusStarting,resp.Devcontainers[0].Status,"devcontainer is not starting")
480+
require.NotNil(t,resp.Devcontainers[0].Container,"devcontainer should have container reference")
481+
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusStarting,resp.Devcontainers[0].Container.DevcontainerStatus,"container dc status is not starting")
480482

481483
// Allow the devcontainer CLI to continue the up process.
482484
close(tt.devcontainerCLI.continueUp)
@@ -503,6 +505,8 @@ func TestAPI(t *testing.T) {
503505
require.NoError(t,err,"unmarshal response failed after error")
504506
require.Len(t,resp.Devcontainers,1,"expected one devcontainer in response after error")
505507
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusError,resp.Devcontainers[0].Status,"devcontainer is not in an error state after up failure")
508+
require.NotNil(t,resp.Devcontainers[0].Container,"devcontainer should have container reference after up failure")
509+
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusError,resp.Devcontainers[0].Container.DevcontainerStatus,"container dc status is not error after up failure")
506510
return
507511
}
508512

@@ -525,7 +529,9 @@ func TestAPI(t *testing.T) {
525529
err=json.NewDecoder(rec.Body).Decode(&resp)
526530
require.NoError(t,err,"unmarshal response failed after recreation")
527531
require.Len(t,resp.Devcontainers,1,"expected one devcontainer in response after recreation")
528-
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusRunning,resp.Devcontainers[0].Status,"devcontainer is not stopped after recreation")
532+
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusRunning,resp.Devcontainers[0].Status,"devcontainer is not running after recreation")
533+
require.NotNil(t,resp.Devcontainers[0].Container,"devcontainer should have container reference after recreation")
534+
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusRunning,resp.Devcontainers[0].Container.DevcontainerStatus,"container dc status is not running after recreation")
529535
})
530536
}
531537
})
@@ -620,6 +626,7 @@ func TestAPI(t *testing.T) {
620626
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusRunning,dc.Status)
621627
require.NotNil(t,dc.Container)
622628
assert.Equal(t,"runtime-container-1",dc.Container.ID)
629+
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusRunning,dc.Container.DevcontainerStatus)
623630
},
624631
},
625632
{
@@ -660,12 +667,14 @@ func TestAPI(t *testing.T) {
660667
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusStopped,known2.Status)
661668
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusRunning,runtime1.Status)
662669

663-
require.NotNil(t,known1.Container)
664670
assert.Nil(t,known2.Container)
665-
require.NotNil(t,runtime1.Container)
666671

672+
require.NotNil(t,known1.Container)
667673
assert.Equal(t,"known-container-1",known1.Container.ID)
674+
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusRunning,known1.Container.DevcontainerStatus)
675+
require.NotNil(t,runtime1.Container)
668676
assert.Equal(t,"runtime-container-1",runtime1.Container.ID)
677+
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusRunning,runtime1.Container.DevcontainerStatus)
669678
},
670679
},
671680
{
@@ -704,10 +713,12 @@ func TestAPI(t *testing.T) {
704713
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusStopped,nonRunning.Status)
705714

706715
require.NotNil(t,running.Container,"running container should have container reference")
707-
require.NotNil(t,nonRunning.Container,"non-running container should have container reference")
708-
709716
assert.Equal(t,"running-container",running.Container.ID)
717+
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusRunning,running.Container.DevcontainerStatus)
718+
719+
require.NotNil(t,nonRunning.Container,"non-running container should have container reference")
710720
assert.Equal(t,"non-running-container",nonRunning.Container.ID)
721+
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusStopped,nonRunning.Container.DevcontainerStatus)
711722
},
712723
},
713724
{
@@ -743,6 +754,7 @@ func TestAPI(t *testing.T) {
743754
assert.NotEmpty(t,dc2.ConfigPath)
744755
require.NotNil(t,dc2.Container)
745756
assert.Equal(t,"known-container-2",dc2.Container.ID)
757+
assert.Equal(t,codersdk.WorkspaceAgentDevcontainerStatusRunning,dc2.Container.DevcontainerStatus)
746758
},
747759
},
748760
{
@@ -811,9 +823,14 @@ func TestAPI(t *testing.T) {
811823

812824
logger:=slogtest.Make(t,&slogtest.Options{IgnoreErrors:true}).Leveled(slog.LevelDebug)
813825

826+
mClock:=quartz.NewMock(t)
827+
mClock.Set(time.Now()).MustWait(testutil.Context(t,testutil.WaitShort))
828+
tickerTrap:=mClock.Trap().TickerFunc("updaterLoop")
829+
814830
// Setup router with the handler under test.
815831
r:=chi.NewRouter()
816832
apiOptions:= []agentcontainers.Option{
833+
agentcontainers.WithClock(mClock),
817834
agentcontainers.WithLister(tt.lister),
818835
agentcontainers.WithWatcher(watcher.NewNoop()),
819836
}
@@ -838,6 +855,15 @@ func TestAPI(t *testing.T) {
838855

839856
ctx:=testutil.Context(t,testutil.WaitShort)
840857

858+
// Make sure the ticker function has been registered
859+
// before advancing the clock.
860+
tickerTrap.MustWait(ctx).MustRelease(ctx)
861+
tickerTrap.Close()
862+
863+
// Advance the clock to run the updater loop.
864+
_,aw:=mClock.AdvanceNext()
865+
aw.MustWait(ctx)
866+
841867
req:=httptest.NewRequest(http.MethodGet,"/devcontainers",nil).
842868
WithContext(ctx)
843869
rec:=httptest.NewRecorder()

‎coderd/apidoc/docs.go

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/apidoc/swagger.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎coderd/workspaceagents.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ func (api *API) workspaceAgentRecreateDevcontainer(rw http.ResponseWriter, r *ht
956956
}
957957
deferrelease()
958958

959-
err=agentConn.RecreateDevcontainer(ctx,container)
959+
m,err:=agentConn.RecreateDevcontainer(ctx,container)
960960
iferr!=nil {
961961
iferrors.Is(err,context.Canceled) {
962962
httpapi.Write(ctx,rw,http.StatusRequestTimeout, codersdk.Response{
@@ -977,7 +977,7 @@ func (api *API) workspaceAgentRecreateDevcontainer(rw http.ResponseWriter, r *ht
977977
return
978978
}
979979

980-
httpapi.Write(ctx,rw,http.StatusNoContent,nil)
980+
httpapi.Write(ctx,rw,http.StatusAccepted,m)
981981
}
982982

983983
// @Summary Get connection info for workspace agent

‎coderd/workspaceagents_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1483,7 +1483,7 @@ func TestWorkspaceAgentRecreateDevcontainer(t *testing.T) {
14831483

14841484
ctx:=testutil.Context(t,testutil.WaitLong)
14851485

1486-
err:=client.WorkspaceAgentRecreateDevcontainer(ctx,agentID,devContainer.ID)
1486+
_,err:=client.WorkspaceAgentRecreateDevcontainer(ctx,agentID,devContainer.ID)
14871487
ifwantStatus>0 {
14881488
cerr,ok:=codersdk.AsError(err)
14891489
require.True(t,ok,"expected error to be a coder error")

‎codersdk/workspaceagents.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,10 @@ type WorkspaceAgentContainer struct {
450450
// Volumes is a map of "things" mounted into the container. Again, this
451451
// is somewhat implementation-dependent.
452452
Volumesmap[string]string`json:"volumes"`
453+
// DevcontainerStatus is the status of the devcontainer, if this
454+
// container is a devcontainer. This is used to determine if the
455+
// devcontainer is running, stopped, starting, or in an error state.
456+
DevcontainerStatusWorkspaceAgentDevcontainerStatus`json:"devcontainer_status,omitempty"`
453457
// DevcontainerDirty is true if the devcontainer configuration has changed
454458
// since the container was created. This is used to determine if the
455459
// container needs to be rebuilt.
@@ -518,16 +522,20 @@ func (c *Client) WorkspaceAgentListContainers(ctx context.Context, agentID uuid.
518522
}
519523

520524
// WorkspaceAgentRecreateDevcontainer recreates the devcontainer with the given ID.
521-
func (c*Client)WorkspaceAgentRecreateDevcontainer(ctx context.Context,agentID uuid.UUID,containerIDOrNamestring)error {
525+
func (c*Client)WorkspaceAgentRecreateDevcontainer(ctx context.Context,agentID uuid.UUID,containerIDOrNamestring)(Response,error) {
522526
res,err:=c.Request(ctx,http.MethodPost,fmt.Sprintf("/api/v2/workspaceagents/%s/containers/devcontainers/container/%s/recreate",agentID,containerIDOrName),nil)
523527
iferr!=nil {
524-
returnerr
528+
returnResponse{},err
525529
}
526530
deferres.Body.Close()
527-
ifres.StatusCode!=http.StatusNoContent {
528-
returnReadBodyAsError(res)
531+
ifres.StatusCode!=http.StatusAccepted {
532+
returnResponse{},ReadBodyAsError(res)
529533
}
530-
returnnil
534+
varmResponse
535+
iferr:=json.NewDecoder(res.Body).Decode(&m);err!=nil {
536+
returnResponse{},xerrors.Errorf("decode response body: %w",err)
537+
}
538+
returnm,nil
531539
}
532540

533541
//nolint:revive // Follow is a control flag on the server as well.

‎codersdk/workspacesdk/agentconn.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,18 +389,22 @@ func (c *AgentConn) ListContainers(ctx context.Context) (codersdk.WorkspaceAgent
389389

390390
// RecreateDevcontainer recreates a devcontainer with the given container.
391391
// This is a blocking call and will wait for the container to be recreated.
392-
func (c*AgentConn)RecreateDevcontainer(ctx context.Context,containerIDOrNamestring)error {
392+
func (c*AgentConn)RecreateDevcontainer(ctx context.Context,containerIDOrNamestring)(codersdk.Response,error) {
393393
ctx,span:=tracing.StartSpan(ctx)
394394
deferspan.End()
395395
res,err:=c.apiRequest(ctx,http.MethodPost,"/api/v0/containers/devcontainers/container/"+containerIDOrName+"/recreate",nil)
396396
iferr!=nil {
397-
returnxerrors.Errorf("do request: %w",err)
397+
returncodersdk.Response{},xerrors.Errorf("do request: %w",err)
398398
}
399399
deferres.Body.Close()
400400
ifres.StatusCode!=http.StatusAccepted {
401-
returncodersdk.ReadBodyAsError(res)
401+
return codersdk.Response{},codersdk.ReadBodyAsError(res)
402402
}
403-
returnnil
403+
varm codersdk.Response
404+
iferr:=json.NewDecoder(res.Body).Decode(&m);err!=nil {
405+
return codersdk.Response{},xerrors.Errorf("decode response body: %w",err)
406+
}
407+
returnm,nil
404408
}
405409

406410
// apiRequest makes a request to the workspace agent's HTTP API server.

‎docs/reference/api/agents.md

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp