mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: notify on task auto pause, manual pause and manual resume (#22050)
This commit is contained in:
@@ -231,6 +231,7 @@ func (e *Executor) runOnce(t time.Time) Stats {
|
||||
job *database.ProvisionerJob
|
||||
auditLog *auditParams
|
||||
shouldNotifyDormancy bool
|
||||
shouldNotifyTaskPause bool
|
||||
nextBuild *database.WorkspaceBuild
|
||||
activeTemplateVersion database.TemplateVersion
|
||||
ws database.Workspace
|
||||
@@ -316,6 +317,10 @@ func (e *Executor) runOnce(t time.Time) Stats {
|
||||
return nil
|
||||
}
|
||||
|
||||
if reason == database.BuildReasonTaskAutoPause {
|
||||
shouldNotifyTaskPause = true
|
||||
}
|
||||
|
||||
// Get the template version job to access tags
|
||||
templateVersionJob, err := tx.GetProvisionerJobByID(e.ctx, activeTemplateVersion.JobID)
|
||||
if err != nil {
|
||||
@@ -482,6 +487,28 @@ func (e *Executor) runOnce(t time.Time) Stats {
|
||||
log.Warn(e.ctx, "failed to notify of workspace marked as dormant", slog.Error(err), slog.F("workspace_id", ws.ID))
|
||||
}
|
||||
}
|
||||
if shouldNotifyTaskPause {
|
||||
task, err := e.db.GetTaskByID(e.ctx, ws.TaskID.UUID)
|
||||
if err != nil {
|
||||
log.Warn(e.ctx, "failed to get task for pause notification", slog.Error(err), slog.F("task_id", ws.TaskID.UUID), slog.F("workspace_id", ws.ID))
|
||||
} else {
|
||||
if _, err := e.notificationsEnqueuer.Enqueue(
|
||||
e.ctx,
|
||||
ws.OwnerID,
|
||||
notifications.TemplateTaskPaused,
|
||||
map[string]string{
|
||||
"task": task.Name,
|
||||
"task_id": task.ID.String(),
|
||||
"workspace": ws.Name,
|
||||
"pause_reason": "inactivity exceeded the dormancy threshold",
|
||||
},
|
||||
"lifecycle_executor",
|
||||
ws.ID, ws.OwnerID, ws.OrganizationID,
|
||||
); err != nil {
|
||||
log.Warn(e.ctx, "failed to notify of task paused", slog.Error(err), slog.F("task_id", ws.TaskID.UUID), slog.F("workspace_id", ws.ID))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if err != nil && !xerrors.Is(err, context.Canceled) {
|
||||
|
||||
@@ -2026,4 +2026,62 @@ func TestExecutorTaskWorkspace(t *testing.T) {
|
||||
workspace = coderdtest.MustWorkspace(t, client, workspace.ID)
|
||||
assert.Equal(t, codersdk.BuildReasonTaskAutoPause, workspace.LatestBuild.Reason, "task workspace should use TaskAutoPause build reason")
|
||||
})
|
||||
|
||||
t.Run("AutostopNotification", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
tickCh = make(chan time.Time)
|
||||
statsCh = make(chan autobuild.Stats)
|
||||
notifyEnq = notificationstest.FakeEnqueuer{}
|
||||
client, db = coderdtest.NewWithDatabase(t, &coderdtest.Options{
|
||||
AutobuildTicker: tickCh,
|
||||
IncludeProvisionerDaemon: true,
|
||||
AutobuildStats: statsCh,
|
||||
NotificationsEnqueuer: ¬ifyEnq,
|
||||
})
|
||||
admin = coderdtest.CreateFirstUser(t, client)
|
||||
)
|
||||
|
||||
// Given: A task workspace with an 8 hour deadline
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
template := createTaskTemplate(t, client, admin.OrganizationID, ctx, 8*time.Hour)
|
||||
workspace := createTaskWorkspace(t, client, template, ctx, "test task for autostop notification")
|
||||
|
||||
// Given: The workspace is currently running
|
||||
workspace = coderdtest.MustWorkspace(t, client, workspace.ID)
|
||||
require.Equal(t, codersdk.WorkspaceTransitionStart, workspace.LatestBuild.Transition)
|
||||
require.NotZero(t, workspace.LatestBuild.Deadline, "workspace should have a deadline for autostop")
|
||||
|
||||
p, err := coderdtest.GetProvisionerForTags(db, time.Now(), workspace.OrganizationID, map[string]string{})
|
||||
require.NoError(t, err)
|
||||
|
||||
// When: the autobuild executor ticks after the deadline
|
||||
go func() {
|
||||
tickTime := workspace.LatestBuild.Deadline.Time.Add(time.Minute)
|
||||
coderdtest.UpdateProvisionerLastSeenAt(t, db, p.ID, tickTime)
|
||||
tickCh <- tickTime
|
||||
close(tickCh)
|
||||
}()
|
||||
|
||||
// Then: We expect to see a stop transition
|
||||
stats := <-statsCh
|
||||
require.Len(t, stats.Transitions, 1, "lifecycle executor should transition the task workspace")
|
||||
assert.Contains(t, stats.Transitions, workspace.ID, "task workspace should be in transitions")
|
||||
assert.Equal(t, database.WorkspaceTransitionStop, stats.Transitions[workspace.ID], "should autostop the workspace")
|
||||
require.Empty(t, stats.Errors, "should have no errors when managing task workspaces")
|
||||
|
||||
// Then: A task paused notification was sent with "idle timeout" reason
|
||||
require.True(t, workspace.TaskID.Valid, "workspace should have a task ID")
|
||||
task, err := db.GetTaskByID(dbauthz.AsSystemRestricted(ctx), workspace.TaskID.UUID)
|
||||
require.NoError(t, err)
|
||||
|
||||
sent := notifyEnq.Sent(notificationstest.WithTemplateID(notifications.TemplateTaskPaused))
|
||||
require.Len(t, sent, 1)
|
||||
require.Equal(t, workspace.OwnerID, sent[0].UserID)
|
||||
require.Equal(t, task.Name, sent[0].Labels["task"])
|
||||
require.Equal(t, task.ID.String(), sent[0].Labels["task_id"])
|
||||
require.Equal(t, workspace.Name, sent[0].Labels["workspace"])
|
||||
require.Equal(t, "inactivity exceeded the dormancy threshold", sent[0].Labels["pause_reason"])
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user