7
7
"errors"
8
8
"fmt"
9
9
"net/http"
10
+ "slices"
10
11
"strconv"
11
12
"time"
12
13
@@ -15,6 +16,7 @@ import (
15
16
"golang.org/x/xerrors"
16
17
17
18
"cdr.dev/slog"
19
+ "github.com/coder/coder/v2/agent/proto"
18
20
"github.com/coder/coder/v2/coderd/audit"
19
21
"github.com/coder/coder/v2/coderd/database"
20
22
"github.com/coder/coder/v2/coderd/database/db2sdk"
@@ -1105,7 +1107,9 @@ func (api *API) putExtendWorkspace(rw http.ResponseWriter, r *http.Request) {
1105
1107
// @ID post-workspace-usage-by-id
1106
1108
// @Security CoderSessionToken
1107
1109
// @Tags Workspaces
1110
+ // @Accept json
1108
1111
// @Param workspace path string true "Workspace ID" format(uuid)
1112
+ // @Param request body codersdk.PostWorkspaceUsageRequest false "Post workspace usage request"
1109
1113
// @Success 204
1110
1114
// @Router /workspaces/{workspace}/usage [post]
1111
1115
func (api * API ) postWorkspaceUsage (rw http.ResponseWriter , r * http.Request ) {
@@ -1116,6 +1120,102 @@ func (api *API) postWorkspaceUsage(rw http.ResponseWriter, r *http.Request) {
1116
1120
}
1117
1121
1118
1122
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
+
1119
1219
rw .WriteHeader (http .StatusNoContent )
1120
1220
}
1121
1221
0 commit comments