mirror of https://github.com/coder/coder.git
Add build number to workspace_build audit logs (#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
This commit is contained in:
parent
6651c1632d
commit
df389d429c
|
@ -160,6 +160,11 @@ func (api *API) convertAuditLogs(ctx context.Context, dblogs []database.GetAudit
|
|||
return alogs
|
||||
}
|
||||
|
||||
type AdditionalFields struct {
|
||||
WorkspaceName string
|
||||
BuildNumber string
|
||||
}
|
||||
|
||||
func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogsOffsetRow) codersdk.AuditLog {
|
||||
ip, _ := netip.AddrFromSlice(dblog.Ip.IPNet.IP)
|
||||
|
||||
|
@ -185,12 +190,29 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
|
|||
}
|
||||
}
|
||||
|
||||
isDeleted := api.auditLogIsResourceDeleted(ctx, dblog)
|
||||
var resourceLink string
|
||||
var (
|
||||
additionalFieldsBytes = []byte(dblog.AdditionalFields)
|
||||
additionalFields AdditionalFields
|
||||
err = json.Unmarshal(additionalFieldsBytes, &additionalFields)
|
||||
)
|
||||
if err != nil {
|
||||
api.Logger.Error(ctx, "unmarshal additional fields", slog.Error(err))
|
||||
resourceInfo := map[string]string{
|
||||
"workspaceName": "unknown",
|
||||
"buildNumber": "unknown",
|
||||
}
|
||||
dblog.AdditionalFields, err = json.Marshal(resourceInfo)
|
||||
api.Logger.Error(ctx, "marshal additional fields", slog.Error(err))
|
||||
}
|
||||
|
||||
var (
|
||||
isDeleted = api.auditLogIsResourceDeleted(ctx, dblog)
|
||||
resourceLink string
|
||||
)
|
||||
if isDeleted {
|
||||
resourceLink = ""
|
||||
} else {
|
||||
resourceLink = api.auditLogResourceLink(ctx, dblog)
|
||||
resourceLink = auditLogResourceLink(dblog, additionalFields)
|
||||
}
|
||||
|
||||
return codersdk.AuditLog{
|
||||
|
@ -209,23 +231,28 @@ func (api *API) convertAuditLog(ctx context.Context, dblog database.GetAuditLogs
|
|||
StatusCode: dblog.StatusCode,
|
||||
AdditionalFields: dblog.AdditionalFields,
|
||||
User: user,
|
||||
Description: auditLogDescription(dblog),
|
||||
Description: auditLogDescription(dblog, additionalFields),
|
||||
ResourceLink: resourceLink,
|
||||
IsDeleted: isDeleted,
|
||||
}
|
||||
}
|
||||
|
||||
func auditLogDescription(alog database.GetAuditLogsOffsetRow) string {
|
||||
func auditLogDescription(alog database.GetAuditLogsOffsetRow, additionalFields AdditionalFields) string {
|
||||
str := fmt.Sprintf("{user} %s",
|
||||
codersdk.AuditAction(alog.Action).FriendlyString(),
|
||||
)
|
||||
|
||||
// Strings for starting/stopping workspace builds follow the below format:
|
||||
// "{user} started build for workspace {target}"
|
||||
// "{user} started build #{build_number} for workspace {target}"
|
||||
// where target is a workspace instead of a workspace build
|
||||
// passed in on the FE via AuditLog.AdditionalFields rather than derived in request.go:35
|
||||
if alog.ResourceType == database.ResourceTypeWorkspaceBuild && alog.Action != database.AuditActionDelete {
|
||||
str += " build for"
|
||||
if len(additionalFields.BuildNumber) == 0 {
|
||||
str += " build for"
|
||||
} else {
|
||||
str += fmt.Sprintf(" build #%s for",
|
||||
additionalFields.BuildNumber)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
|||
}
|
||||
}
|
||||
|
||||
type AdditionalFields struct {
|
||||
WorkspaceName string
|
||||
BuildNumber string
|
||||
}
|
||||
|
||||
func (api *API) auditLogResourceLink(ctx context.Context, alog database.GetAuditLogsOffsetRow) string {
|
||||
func auditLogResourceLink(alog database.GetAuditLogsOffsetRow, additionalFields AdditionalFields) string {
|
||||
switch alog.ResourceType {
|
||||
case database.ResourceTypeTemplate:
|
||||
return fmt.Sprintf("/templates/%s",
|
||||
|
@ -312,11 +334,8 @@ func (api *API) auditLogResourceLink(ctx context.Context, alog database.GetAudit
|
|||
return fmt.Sprintf("/@%s/%s",
|
||||
alog.UserUsername.String, alog.ResourceTarget)
|
||||
case database.ResourceTypeWorkspaceBuild:
|
||||
additionalFieldsBytes := []byte(alog.AdditionalFields)
|
||||
var additionalFields AdditionalFields
|
||||
err := json.Unmarshal(additionalFieldsBytes, &additionalFields)
|
||||
if err != nil {
|
||||
api.Logger.Error(ctx, "unmarshal workspace name", slog.Error(err))
|
||||
if len(additionalFields.WorkspaceName) == 0 || len(additionalFields.BuildNumber) == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("/@%s/%s/builds/%s",
|
||||
alog.UserUsername.String, additionalFields.WorkspaceName, additionalFields.BuildNumber)
|
||||
|
|
|
@ -531,23 +531,23 @@ func (server *Server) FailJob(ctx context.Context, failJob *proto.FailedJob) (*p
|
|||
auditor := server.Auditor.Load()
|
||||
build, getBuildErr := server.Database.GetWorkspaceBuildByJobID(ctx, job.ID)
|
||||
if getBuildErr != nil {
|
||||
server.Logger.Error(ctx, "failed to create audit log - get build err", slog.Error(err))
|
||||
server.Logger.Error(ctx, "audit log - get build", slog.Error(err))
|
||||
} else {
|
||||
auditAction := auditActionFromTransition(build.Transition)
|
||||
workspace, getWorkspaceErr := server.Database.GetWorkspaceByID(ctx, build.WorkspaceID)
|
||||
if getWorkspaceErr != nil {
|
||||
server.Logger.Error(ctx, "failed to create audit log - get workspace err", slog.Error(err))
|
||||
server.Logger.Error(ctx, "audit log - get workspace", slog.Error(err))
|
||||
} else {
|
||||
// We pass the below information to the Auditor so that it
|
||||
// can form a friendly string for the user to view in the UI.
|
||||
workspaceResourceInfo := map[string]string{
|
||||
buildResourceInfo := map[string]string{
|
||||
"workspaceName": workspace.Name,
|
||||
"buildNumber": strconv.FormatInt(int64(build.BuildNumber), 10),
|
||||
}
|
||||
|
||||
wriBytes, err := json.Marshal(workspaceResourceInfo)
|
||||
wriBytes, err := json.Marshal(buildResourceInfo)
|
||||
if err != nil {
|
||||
server.Logger.Error(ctx, "could not marshal workspace name", slog.Error(err))
|
||||
server.Logger.Error(ctx, "marshal workspace resource info for failed job", slog.Error(err))
|
||||
}
|
||||
|
||||
audit.BuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{
|
||||
|
@ -756,14 +756,14 @@ func (server *Server) CompleteJob(ctx context.Context, completed *proto.Complete
|
|||
|
||||
// We pass the below information to the Auditor so that it
|
||||
// can form a friendly string for the user to view in the UI.
|
||||
workspaceResourceInfo := map[string]string{
|
||||
buildResourceInfo := map[string]string{
|
||||
"workspaceName": workspace.Name,
|
||||
"buildNumber": strconv.FormatInt(int64(workspaceBuild.BuildNumber), 10),
|
||||
}
|
||||
|
||||
wriBytes, err := json.Marshal(workspaceResourceInfo)
|
||||
wriBytes, err := json.Marshal(buildResourceInfo)
|
||||
if err != nil {
|
||||
server.Logger.Error(ctx, "marshal resource info", slog.Error(err))
|
||||
server.Logger.Error(ctx, "marshal resource info for successful job", slog.Error(err))
|
||||
}
|
||||
|
||||
audit.BuildAudit(ctx, &audit.BuildAuditParams[database.WorkspaceBuild]{
|
||||
|
|
|
@ -11,7 +11,7 @@ We track **create, update and delete** events for the following resources:
|
|||
- Template
|
||||
- TemplateVersion
|
||||
- Workspace
|
||||
- Workspace start/stop
|
||||
- WorkspaceBuild
|
||||
- User
|
||||
- Group
|
||||
|
||||
|
|
|
@ -14,10 +14,7 @@ export const AuditLogDescription: FC<{ auditLog: AuditLog }> = ({
|
|||
let target = auditLog.resource_target.trim()
|
||||
|
||||
// audit logs with a resource_type of workspace build use workspace name as a target
|
||||
if (
|
||||
auditLog.resource_type === "workspace_build" &&
|
||||
auditLog.additional_fields.workspaceName
|
||||
) {
|
||||
if (auditLog.resource_type === "workspace_build") {
|
||||
target = auditLog.additional_fields.workspaceName.trim()
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue