Skip to content

Commit a3330c7

Browse files
committed
add test for agent app health routes
1 parent fec256d commit a3330c7

File tree

4 files changed

+132
-17
lines changed

4 files changed

+132
-17
lines changed

agent/apphealth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func NewWorkspaceAppHealthReporter(logger slog.Logger, client *codersdk.Client)
5454
}
5555
}
5656
var mu sync.RWMutex
57-
var failures map[string]int
57+
failures := make(map[string]int, 0)
5858
go func() {
5959
for {
6060
select {

coderd/workspaceagents.go

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,13 @@ func (api *API) postWorkspaceAppHealth(rw http.ResponseWriter, r *http.Request)
701701
return
702702
}
703703

704+
if req.Healths == nil || len(req.Healths) == 0 {
705+
httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{
706+
Message: "Health field is empty",
707+
})
708+
return
709+
}
710+
704711
apps, err := api.Database.GetWorkspaceAppsByAgentID(r.Context(), workspaceAgent.ID)
705712
if err != nil {
706713
httpapi.Write(rw, http.StatusInternalServerError, codersdk.Response{
@@ -711,8 +718,8 @@ func (api *API) postWorkspaceAppHealth(rw http.ResponseWriter, r *http.Request)
711718
}
712719

713720
var newApps []database.WorkspaceApp
714-
for name, health := range req.Healths {
715-
found := func() *database.WorkspaceApp {
721+
for name, newHealth := range req.Healths {
722+
old := func() *database.WorkspaceApp {
716723
for _, app := range apps {
717724
if app.Name == name {
718725
return &app
@@ -721,43 +728,41 @@ func (api *API) postWorkspaceAppHealth(rw http.ResponseWriter, r *http.Request)
721728

722729
return nil
723730
}()
724-
if found == nil {
731+
if old == nil {
725732
httpapi.Write(rw, http.StatusNotFound, codersdk.Response{
726733
Message: "Error setting workspace app health",
727734
Detail: xerrors.Errorf("workspace app name %s not found", name).Error(),
728735
})
729736
return
730737
}
731738

732-
if !found.HealthcheckEnabled {
739+
if !old.HealthcheckEnabled {
733740
httpapi.Write(rw, http.StatusNotFound, codersdk.Response{
734741
Message: "Error setting workspace app health",
735742
Detail: xerrors.Errorf("health checking is disabled for workspace app %s", name).Error(),
736743
})
737744
return
738745
}
739746

740-
switch health {
747+
switch newHealth {
741748
case codersdk.WorkspaceAppHealthInitializing:
742-
found.Health = database.WorkspaceAppHealthInitializing
743749
case codersdk.WorkspaceAppHealthHealthy:
744-
found.Health = database.WorkspaceAppHealthHealthy
745750
case codersdk.WorkspaceAppHealthUnhealthy:
746-
found.Health = database.WorkspaceAppHealthUnhealthy
747751
default:
748752
httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{
749753
Message: "Error setting workspace app health",
750-
Detail: xerrors.Errorf("workspace app health %s is not a valid value", health).Error(),
754+
Detail: xerrors.Errorf("workspace app health %s is not a valid value", newHealth).Error(),
751755
})
752756
return
753757
}
754758

755759
// don't save if the value hasn't changed
756-
if found.Health == database.WorkspaceAppHealth(health) {
760+
if old.Health == database.WorkspaceAppHealth(newHealth) {
757761
continue
758762
}
763+
old.Health = database.WorkspaceAppHealth(newHealth)
759764

760-
newApps = append(newApps, *found)
765+
newApps = append(newApps, *old)
761766
}
762767

763768
for _, app := range newApps {

coderd/workspaceagents_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,3 +363,111 @@ func TestWorkspaceAgentPTY(t *testing.T) {
363363
expectLine(matchEchoCommand)
364364
expectLine(matchEchoOutput)
365365
}
366+
367+
func TestWorkspaceAgentAppHealth(t *testing.T) {
368+
t.Parallel()
369+
client := coderdtest.New(t, &coderdtest.Options{
370+
IncludeProvisionerDaemon: true,
371+
})
372+
user := coderdtest.CreateFirstUser(t, client)
373+
authToken := uuid.NewString()
374+
apps := []*proto.App{
375+
{
376+
Name: "code-server",
377+
Command: "some-command",
378+
Url: "http://localhost:3000",
379+
Icon: "/code.svg",
380+
},
381+
{
382+
Name: "code-server-2",
383+
Command: "some-command",
384+
Url: "http://localhost:3000",
385+
Icon: "/code.svg",
386+
HealthcheckEnabled: true,
387+
HealthcheckUrl: "http://localhost:3000",
388+
HealthcheckInterval: 5,
389+
HealthcheckThreshold: 6,
390+
},
391+
}
392+
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
393+
Parse: echo.ParseComplete,
394+
Provision: []*proto.Provision_Response{{
395+
Type: &proto.Provision_Response_Complete{
396+
Complete: &proto.Provision_Complete{
397+
Resources: []*proto.Resource{{
398+
Name: "example",
399+
Type: "aws_instance",
400+
Agents: []*proto.Agent{{
401+
Id: uuid.NewString(),
402+
Auth: &proto.Agent_Token{
403+
Token: authToken,
404+
},
405+
Apps: apps,
406+
}},
407+
}},
408+
},
409+
},
410+
}},
411+
})
412+
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
413+
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
414+
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
415+
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
416+
417+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
418+
defer cancel()
419+
420+
agentClient := codersdk.New(client.URL)
421+
agentClient.SessionToken = authToken
422+
423+
apiApps, err := agentClient.WorkspaceAgentApps(ctx)
424+
require.NoError(t, err)
425+
require.EqualValues(t, codersdk.WorkspaceAppHealthDisabled, apiApps[0].Health)
426+
require.EqualValues(t, codersdk.WorkspaceAppHealthInitializing, apiApps[1].Health)
427+
err = agentClient.PostWorkspaceAgentAppHealth(ctx, codersdk.PostWorkspaceAppHealthsRequest{})
428+
require.Error(t, err)
429+
// empty
430+
err = agentClient.PostWorkspaceAgentAppHealth(ctx, codersdk.PostWorkspaceAppHealthsRequest{})
431+
require.Error(t, err)
432+
// invalid name
433+
err = agentClient.PostWorkspaceAgentAppHealth(ctx, codersdk.PostWorkspaceAppHealthsRequest{
434+
Healths: map[string]codersdk.WorkspaceAppHealth{
435+
"bad-name": codersdk.WorkspaceAppHealthDisabled,
436+
},
437+
})
438+
require.Error(t, err)
439+
// app.HealthEnabled == false
440+
err = agentClient.PostWorkspaceAgentAppHealth(ctx, codersdk.PostWorkspaceAppHealthsRequest{
441+
Healths: map[string]codersdk.WorkspaceAppHealth{
442+
"code-server": codersdk.WorkspaceAppHealthInitializing,
443+
},
444+
})
445+
require.Error(t, err)
446+
// invalid value
447+
err = agentClient.PostWorkspaceAgentAppHealth(ctx, codersdk.PostWorkspaceAppHealthsRequest{
448+
Healths: map[string]codersdk.WorkspaceAppHealth{
449+
"code-server-2": codersdk.WorkspaceAppHealth("bad-value"),
450+
},
451+
})
452+
require.Error(t, err)
453+
// update to healthy
454+
err = agentClient.PostWorkspaceAgentAppHealth(ctx, codersdk.PostWorkspaceAppHealthsRequest{
455+
Healths: map[string]codersdk.WorkspaceAppHealth{
456+
"code-server-2": codersdk.WorkspaceAppHealthHealthy,
457+
},
458+
})
459+
require.NoError(t, err)
460+
apiApps, err = agentClient.WorkspaceAgentApps(ctx)
461+
require.NoError(t, err)
462+
require.EqualValues(t, codersdk.WorkspaceAppHealthHealthy, apiApps[1].Health)
463+
// update to unhealthy
464+
err = agentClient.PostWorkspaceAgentAppHealth(ctx, codersdk.PostWorkspaceAppHealthsRequest{
465+
Healths: map[string]codersdk.WorkspaceAppHealth{
466+
"code-server-2": codersdk.WorkspaceAppHealthUnhealthy,
467+
},
468+
})
469+
require.NoError(t, err)
470+
apiApps, err = agentClient.WorkspaceAgentApps(ctx)
471+
require.NoError(t, err)
472+
require.EqualValues(t, codersdk.WorkspaceAppHealthUnhealthy, apiApps[1].Health)
473+
}

codersdk/workspaceagents.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ func (c *Client) WorkspaceAgent(ctx context.Context, id uuid.UUID) (WorkspaceAge
363363

364364
// MyWorkspaceAgent returns the requesting agent.
365365
func (c *Client) WorkspaceAgentApps(ctx context.Context) ([]WorkspaceApp, error) {
366-
res, err := c.Request(ctx, http.MethodGet, "/api/v2/workspaceagents/me", nil)
366+
res, err := c.Request(ctx, http.MethodGet, "/api/v2/workspaceagents/me/apps", nil)
367367
if err != nil {
368368
return nil, err
369369
}
@@ -377,13 +377,15 @@ func (c *Client) WorkspaceAgentApps(ctx context.Context) ([]WorkspaceApp, error)
377377

378378
// PostWorkspaceAgentAppHealth updates the workspace agent app health status.
379379
func (c *Client) PostWorkspaceAgentAppHealth(ctx context.Context, req PostWorkspaceAppHealthsRequest) error {
380-
res, err := c.Request(ctx, http.MethodPost, "/api/v2/workspaceagents/me/version", req)
380+
res, err := c.Request(ctx, http.MethodPost, "/api/v2/workspaceagents/me/app-health", req)
381381
if err != nil {
382+
return err
383+
}
384+
defer res.Body.Close()
385+
if res.StatusCode != http.StatusOK {
382386
return readBodyAsError(res)
383387
}
384-
// Discord the response
385-
_, _ = io.Copy(io.Discard, res.Body)
386-
_ = res.Body.Close()
388+
387389
return nil
388390
}
389391

0 commit comments

Comments
 (0)