Skip to content

Commit df389d4

Browse files
authored
Add build number to workspace_build audit logs (coder#5267)
* got links working * added translations * fixed translation * added translation for unavailable ip * added support for group, template, user links * cleaned up string * added deleted label * querying for workspace id * remove prints * fix/write tests * added build number * checking for existence of additional fields * adjust documentation * PR feedback
1 parent 6651c16 commit df389d4

File tree

4 files changed

+47
-31
lines changed

4 files changed

+47
-31
lines changed

coderd/audit.go

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,11 @@ func (api *API) convertAuditLogs(ctx context.Context, dblogs []database.GetAudit
160160
return alogs
161161
}
162162

163+
type AdditionalFields struct {
164+
WorkspaceName string
165+
BuildNumber string
166+
}
167+
163168
func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogsOffsetRow) codersdk.AuditLog {
164169
ip, _ := netip.AddrFromSlice(dblog.Ip.IPNet.IP)
165170

@@ -185,12 +190,29 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
185190
}
186191
}
187192

188-
isDeleted := api.auditLogIsResourceDeleted(ctx, dblog)
189-
var resourceLink string
193+
var (
194+
additionalFieldsBytes = []byte(dblog.AdditionalFields)
195+
additionalFields AdditionalFields
196+
err = json.Unmarshal(additionalFieldsBytes, &additionalFields)
197+
)
198+
if err != nil {
199+
api.Logger.Error(ctx, "unmarshal additional fields", slog.Error(err))
200+
resourceInfo := map[string]string{
201+
"workspaceName": "unknown",
202+
"buildNumber": "unknown",
203+
}
204+
dblog.AdditionalFields, err = json.Marshal(resourceInfo)
205+
api.Logger.Error(ctx, "marshal additional fields", slog.Error(err))
206+
}
207+
208+
var (
209+
isDeleted = api.auditLogIsResourceDeleted(ctx, dblog)
210+
resourceLink string
211+
)
190212
if isDeleted {
191213
resourceLink = ""
192214
} else {
193-
resourceLink = api.auditLogResourceLink(ctx, dblog)
215+
resourceLink = auditLogResourceLink(dblog, additionalFields)
194216
}
195217

196218
return codersdk.AuditLog{
@@ -209,23 +231,28 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
209231
StatusCode: dblog.StatusCode,
210232
AdditionalFields: dblog.AdditionalFields,
211233
User: user,
212-
Description: auditLogDescription(dblog),
234+
Description: auditLogDescription(dblog, additionalFields),
213235
ResourceLink: resourceLink,
214236
IsDeleted: isDeleted,
215237
}
216238
}
217239

218-
func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
240+
func auditLogDescription(alog database.GetAuditLogsOffsetRow, additionalFields AdditionalFields) string {
219241
str := fmt.Sprintf("{user} %s",
220242
codersdk.AuditAction(alog.Action).FriendlyString(),
221243
)
222244

223245
// Strings for starting/stopping workspace builds follow the below format:
224-
// "{user} started build for workspace {target}"
246+
// "{user} started build #{build_number} for workspace {target}"
225247
// where target is a workspace instead of a workspace build
226248
// passed in on the FE via AuditLog.AdditionalFields rather than derived in request.go:35
227249
if alog.ResourceType == database.ResourceTypeWorkspaceBuild && alog.Action != database.AuditActionDelete {
228-
str += " build for"
250+
if len(additionalFields.BuildNumber) == 0 {
251+
str += " build for"
252+
} else {
253+
str += fmt.Sprintf(" build #%s for",
254+
additionalFields.BuildNumber)
255+
}
229256
}
230257

231258
// We don't display the name (target) for git ssh keys. It's fairly long and doesn't
@@ -295,12 +322,7 @@ func (api *API) auditLogIsResourceDeleted(ctx context.Context, alog database.Get
295322
}
296323
}
297324

298-
type AdditionalFields struct {
299-
WorkspaceName string
300-
BuildNumber string
301-
}
302-
303-
func (api *API) auditLogResourceLink(ctx context.Context, alog database.GetAuditLogsOffsetRow) string {
325+
func auditLogResourceLink(alog database.GetAuditLogsOffsetRow, additionalFields AdditionalFields) string {
304326
switch alog.ResourceType {
305327
case database.ResourceTypeTemplate:
306328
return fmt.Sprintf("/templates/%s",
@@ -312,11 +334,8 @@ func (api *API) auditLogResourceLink(ctx context.Context, alog database.GetAudit
312334
return fmt.Sprintf("/@%s/%s",
313335
alog.UserUsername.String, alog.ResourceTarget)
314336
case database.ResourceTypeWorkspaceBuild:
315-
additionalFieldsBytes := []byte(alog.AdditionalFields)
316-
var additionalFields AdditionalFields
317-
err := json.Unmarshal(additionalFieldsBytes, &additionalFields)
318-
if err != nil {
319-
api.Logger.Error(ctx, "unmarshal workspace name", slog.Error(err))
337+
if len(additionalFields.WorkspaceName) == 0 || len(additionalFields.BuildNumber) == 0 {
338+
return ""
320339
}
321340
return fmt.Sprintf("/@%s/%s/builds/%s",
322341
alog.UserUsername.String, additionalFields.WorkspaceName, additionalFields.BuildNumber)

coderd/provisionerdserver/provisionerdserver.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -531,23 +531,23 @@ func (server *Server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*p
531531
auditor := server.Auditor.Load()
532532
build, getBuildErr := server.Database.GetWorkspaceBuildByJobID(ctx, job.ID)
533533
if getBuildErr != nil {
534-
server.Logger.Error(ctx, "failed to create audit log - get build err", slog.Error(err))
534+
server.Logger.Error(ctx, "audit log - get build", slog.Error(err))
535535
} else {
536536
auditAction := auditActionFromTransition(build.Transition)
537537
workspace, getWorkspaceErr := server.Database.GetWorkspaceByID(ctx, build.WorkspaceID)
538538
if getWorkspaceErr != nil {
539-
server.Logger.Error(ctx, "failed to create audit log - get workspace err", slog.Error(err))
539+
server.Logger.Error(ctx, "audit log - get workspace", slog.Error(err))
540540
} else {
541541
// We pass the below information to the Auditor so that it
542542
// can form a friendly string for the user to view in the UI.
543-
workspaceResourceInfo := map[string]string{
543+
buildResourceInfo := map[string]string{
544544
"workspaceName": workspace.Name,
545545
"buildNumber": strconv.FormatInt(int64(build.BuildNumber), 10),
546546
}
547547

548-
wriBytes, err := json.Marshal(workspaceResourceInfo)
548+
wriBytes, err := json.Marshal(buildResourceInfo)
549549
if err != nil {
550-
server.Logger.Error(ctx, "could not marshal workspace name", slog.Error(err))
550+
server.Logger.Error(ctx, "marshal workspace resource info for failed job", slog.Error(err))
551551
}
552552

553553
audit.BuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{
@@ -756,14 +756,14 @@ func (server *Server) CompleteJob(ctx context.Context, completed *proto.Complete
756756

757757
// We pass the below information to the Auditor so that it
758758
// can form a friendly string for the user to view in the UI.
759-
workspaceResourceInfo := map[string]string{
759+
buildResourceInfo := map[string]string{
760760
"workspaceName": workspace.Name,
761761
"buildNumber": strconv.FormatInt(int64(workspaceBuild.BuildNumber), 10),
762762
}
763763

764-
wriBytes, err := json.Marshal(workspaceResourceInfo)
764+
wriBytes, err := json.Marshal(buildResourceInfo)
765765
if err != nil {
766-
server.Logger.Error(ctx, "marshal resource info", slog.Error(err))
766+
server.Logger.Error(ctx, "marshal resource info for successful job", slog.Error(err))
767767
}
768768

769769
audit.BuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{

docs/admin/audit-logs.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ We track **create, update and delete** events for the following resources:
1111
- Template
1212
- TemplateVersion
1313
- Workspace
14-
- Workspace start/stop
14+
- WorkspaceBuild
1515
- User
1616
- Group
1717

site/src/components/AuditLogRow/AuditLogDescription.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@ export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({
1414
let target = auditLog.resource_target.trim()
1515

1616
// audit logs with a resource_type of workspace build use workspace name as a target
17-
if (
18-
auditLog.resource_type === "workspace_build" &&
19-
auditLog.additional_fields.workspaceName
20-
) {
17+
if (auditLog.resource_type === "workspace_build") {
2118
target = auditLog.additional_fields.workspaceName.trim()
2219
}
2320

0 commit comments

Comments
 (0)