Skip to content

Commit 44d6913

Browse files
authored
chore: accept payload on workspace usage route (#13544)
1 parent 87820a2 commit 44d6913

File tree

9 files changed

+450
-13
lines changed

9 files changed

+450
-13
lines changed

coderd/apidoc/docs.go

+44-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+37-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/workspaces.go

+100
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"errors"
88
"fmt"
99
"net/http"
10+
"slices"
1011
"strconv"
1112
"time"
1213

@@ -15,6 +16,7 @@ import (
1516
"golang.org/x/xerrors"
1617

1718
"cdr.dev/slog"
19+
"github.com/coder/coder/v2/agent/proto"
1820
"github.com/coder/coder/v2/coderd/audit"
1921
"github.com/coder/coder/v2/coderd/database"
2022
"github.com/coder/coder/v2/coderd/database/db2sdk"
@@ -1105,7 +1107,9 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) {
11051107
// @ID post-workspace-usage-by-id
11061108
// @Security CoderSessionToken
11071109
// @Tags Workspaces
1110+
// @Accept json
11081111
// @Param workspace path string true "Workspace ID" format(uuid)
1112+
// @Param request body codersdk.PostWorkspaceUsageRequest false "Post workspace usage request"
11091113
// @Success 204
11101114
// @Router /workspaces/{workspace}/usage [post]
11111115
func (api *API) postWorkspaceUsage(rw http.ResponseWriter, r *http.Request) {
@@ -1116,6 +1120,102 @@ func (api *API) postWorkspaceUsage(rw http.ResponseWriter, r *http.Request) {
11161120
}
11171121

11181122
api.statsReporter.TrackUsage(workspace.ID)
1123+
1124+
if !api.Experiments.Enabled(codersdk.ExperimentWorkspaceUsage) {
1125+
// Continue previous behavior if the experiment is not enabled.
1126+
rw.WriteHeader(http.StatusNoContent)
1127+
return
1128+
}
1129+
1130+
if r.Body == http.NoBody {
1131+
// Continue previous behavior if no body is present.
1132+
rw.WriteHeader(http.StatusNoContent)
1133+
return
1134+
}
1135+
1136+
ctx := r.Context()
1137+
var req codersdk.PostWorkspaceUsageRequest
1138+
if !httpapi.Read(ctx, rw, r, &req) {
1139+
return
1140+
}
1141+
1142+
if req.AgentID == uuid.Nil && req.AppName == "" {
1143+
// Continue previous behavior if body is empty.
1144+
rw.WriteHeader(http.StatusNoContent)
1145+
return
1146+
}
1147+
if req.AgentID == uuid.Nil {
1148+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
1149+
Message: "Invalid request",
1150+
Validations: []codersdk.ValidationError{{
1151+
Field: "agent_id",
1152+
Detail: "must be set when app_name is set",
1153+
}},
1154+
})
1155+
return
1156+
}
1157+
if req.AppName == "" {
1158+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
1159+
Message: "Invalid request",
1160+
Validations: []codersdk.ValidationError{{
1161+
Field: "app_name",
1162+
Detail: "must be set when agent_id is set",
1163+
}},
1164+
})
1165+
return
1166+
}
1167+
if !slices.Contains(codersdk.AllowedAppNames, req.AppName) {
1168+
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
1169+
Message: "Invalid request",
1170+
Validations: []codersdk.ValidationError{{
1171+
Field: "app_name",
1172+
Detail: fmt.Sprintf("must be one of %v", codersdk.AllowedAppNames),
1173+
}},
1174+
})
1175+
return
1176+
}
1177+
1178+
stat := &proto.Stats{
1179+
ConnectionCount: 1,
1180+
}
1181+
switch req.AppName {
1182+
case codersdk.UsageAppNameVscode:
1183+
stat.SessionCountVscode = 1
1184+
case codersdk.UsageAppNameJetbrains:
1185+
stat.SessionCountJetbrains = 1
1186+
case codersdk.UsageAppNameReconnectingPty:
1187+
stat.SessionCountReconnectingPty = 1
1188+
case codersdk.UsageAppNameSSH:
1189+
stat.SessionCountSsh = 1
1190+
default:
1191+
// This means the app_name is in the codersdk.AllowedAppNames but not being
1192+
// handled by this switch statement.
1193+
httpapi.InternalServerError(rw, xerrors.Errorf("unknown app_name %q", req.AppName))
1194+
return
1195+
}
1196+
1197+
agent, err := api.Database.GetWorkspaceAgentByID(ctx, req.AgentID)
1198+
if err != nil {
1199+
if httpapi.Is404Error(err) {
1200+
httpapi.ResourceNotFound(rw)
1201+
return
1202+
}
1203+
httpapi.InternalServerError(rw, err)
1204+
return
1205+
}
1206+
1207+
template, err := api.Database.GetTemplateByID(ctx, workspace.TemplateID)
1208+
if err != nil {
1209+
httpapi.InternalServerError(rw, err)
1210+
return
1211+
}
1212+
1213+
err = api.statsReporter.ReportAgentStats(ctx, dbtime.Now(), workspace, agent, template.Name, stat)
1214+
if err != nil {
1215+
httpapi.InternalServerError(rw, err)
1216+
return
1217+
}
1218+
11191219
rw.WriteHeader(http.StatusNoContent)
11201220
}
11211221

0 commit comments

Comments
 (0)