mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
fix: prevent notification for dormant delete on dormant-removal (#21427)
Ensure we do not send "Marked for deletion" notifications when disabling dormancy deletion
This commit is contained in:
@@ -140,8 +140,8 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
|
||||
}
|
||||
|
||||
var (
|
||||
template database.Template
|
||||
markedForDeletion []database.WorkspaceTable
|
||||
template database.Template
|
||||
dormantWorkspacesUpdated []database.WorkspaceTable
|
||||
)
|
||||
err = db.InTx(func(tx database.Store) error {
|
||||
ctx, span := tracing.StartSpanWithName(ctx, "(*schedule.EnterpriseTemplateScheduleStore).Set()-InTx()")
|
||||
@@ -176,7 +176,7 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
|
||||
// to ensure workspaces are being cleaned up correctly. Similarly if we are
|
||||
// disabling it (by passing 0), then we want to delete nullify the deleting_at
|
||||
// fields of all the template workspaces.
|
||||
markedForDeletion, err = tx.UpdateWorkspacesDormantDeletingAtByTemplateID(ctx, database.UpdateWorkspacesDormantDeletingAtByTemplateIDParams{
|
||||
dormantWorkspacesUpdated, err = tx.UpdateWorkspacesDormantDeletingAtByTemplateID(ctx, database.UpdateWorkspacesDormantDeletingAtByTemplateIDParams{
|
||||
TemplateID: tpl.ID,
|
||||
TimeTilDormantAutodeleteMs: opts.TimeTilDormantAutoDelete.Milliseconds(),
|
||||
DormantAt: dormantAt,
|
||||
@@ -267,27 +267,30 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
|
||||
}
|
||||
}
|
||||
|
||||
for _, ws := range markedForDeletion {
|
||||
if opts.TimeTilDormantAutoDelete > 0 {
|
||||
dormantTime := s.now().Add(opts.TimeTilDormantAutoDelete)
|
||||
_, err = s.enqueuer.Enqueue(
|
||||
// nolint:gocritic // Need actor to enqueue notification
|
||||
dbauthz.AsNotifier(ctx),
|
||||
ws.OwnerID,
|
||||
notifications.TemplateWorkspaceMarkedForDeletion,
|
||||
map[string]string{
|
||||
"name": ws.Name,
|
||||
"reason": "an update to the template's dormancy",
|
||||
"timeTilDormant": humanize.Time(dormantTime),
|
||||
},
|
||||
"scheduletemplate",
|
||||
// Associate this notification with all the related entities.
|
||||
ws.ID,
|
||||
ws.OwnerID,
|
||||
ws.TemplateID,
|
||||
ws.OrganizationID,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Warn(ctx, "failed to notify of workspace marked for deletion", slog.Error(err), slog.F("workspace_id", ws.ID))
|
||||
|
||||
for _, ws := range dormantWorkspacesUpdated {
|
||||
_, err = s.enqueuer.Enqueue(
|
||||
// nolint:gocritic // Need actor to enqueue notification
|
||||
dbauthz.AsNotifier(ctx),
|
||||
ws.OwnerID,
|
||||
notifications.TemplateWorkspaceMarkedForDeletion,
|
||||
map[string]string{
|
||||
"name": ws.Name,
|
||||
"reason": "an update to the template's dormancy",
|
||||
"timeTilDormant": humanize.Time(dormantTime),
|
||||
},
|
||||
"scheduletemplate",
|
||||
// Associate this notification with all the related entities.
|
||||
ws.ID,
|
||||
ws.OwnerID,
|
||||
ws.TemplateID,
|
||||
ws.OrganizationID,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Warn(ctx, "failed to notify of workspace marked for deletion", slog.Error(err), slog.F("workspace_id", ws.ID))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -836,6 +836,92 @@ func TestNotifications(t *testing.T) {
|
||||
"deleted workspace should not receive notifications")
|
||||
}
|
||||
})
|
||||
|
||||
// Disabling dormancy auto-deletion should not send "marked for deletion" notifications.
|
||||
t.Run("DisablingAutoDeleteSendsNoNotifications", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
db, _ = dbtestutil.NewDB(t)
|
||||
user = dbgen.User(t, db, database.User{})
|
||||
file = dbgen.File(t, db, database.File{
|
||||
CreatedBy: user.ID,
|
||||
})
|
||||
templateJob = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
|
||||
FileID: file.ID,
|
||||
InitiatorID: user.ID,
|
||||
Tags: database.StringMap{
|
||||
"foo": "bar",
|
||||
},
|
||||
})
|
||||
timeTilDormant = time.Minute * 2
|
||||
timeTilDormantAutoDelete = time.Minute * 4
|
||||
templateVersion = dbgen.TemplateVersion(t, db, database.TemplateVersion{
|
||||
CreatedBy: user.ID,
|
||||
JobID: templateJob.ID,
|
||||
OrganizationID: templateJob.OrganizationID,
|
||||
})
|
||||
template = dbgen.Template(t, db, database.Template{
|
||||
ActiveVersionID: templateVersion.ID,
|
||||
CreatedBy: user.ID,
|
||||
OrganizationID: templateJob.OrganizationID,
|
||||
})
|
||||
)
|
||||
|
||||
// Given: Dormancy auto deletion is enabled
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
err := db.UpdateTemplateScheduleByID(ctx, database.UpdateTemplateScheduleByIDParams{
|
||||
ID: template.ID,
|
||||
UpdatedAt: dbtime.Now(),
|
||||
TimeTilDormant: int64(timeTilDormant),
|
||||
TimeTilDormantAutoDelete: int64(timeTilDormantAutoDelete),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Given: A workspace that is marked as dormant
|
||||
workspace := dbgen.Workspace(t, db, database.WorkspaceTable{
|
||||
OwnerID: user.ID,
|
||||
TemplateID: template.ID,
|
||||
OrganizationID: templateJob.OrganizationID,
|
||||
LastUsedAt: time.Now().Add(-time.Hour),
|
||||
})
|
||||
dormantAt := workspace.LastUsedAt.Add(timeTilDormant)
|
||||
workspace, err = db.UpdateWorkspaceDormantDeletingAt(ctx, database.UpdateWorkspaceDormantDeletingAtParams{
|
||||
ID: workspace.ID,
|
||||
DormantAt: sql.NullTime{
|
||||
Time: dormantAt,
|
||||
Valid: true,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.True(t, workspace.DeletingAt.Valid, "deleting_at should be set when marking workspace dormant")
|
||||
|
||||
// Setup dependencies
|
||||
notifyEnq := notificationstest.NewFakeEnqueuer()
|
||||
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
|
||||
const userQuietHoursSchedule = "CRON_TZ=UTC 0 0 * * *" // midnight UTC
|
||||
userQuietHoursStore, err := schedule.NewEnterpriseUserQuietHoursScheduleStore(userQuietHoursSchedule, true)
|
||||
require.NoError(t, err)
|
||||
userQuietHoursStorePtr := &atomic.Pointer[agplschedule.UserQuietHoursScheduleStore]{}
|
||||
userQuietHoursStorePtr.Store(&userQuietHoursStore)
|
||||
templateScheduleStore := schedule.NewEnterpriseTemplateScheduleStore(userQuietHoursStorePtr, notifyEnq, logger, nil)
|
||||
|
||||
// When: We disable dormancy auto-delete
|
||||
_, err = templateScheduleStore.Set(dbauthz.AsNotifier(ctx), db, template, agplschedule.TemplateScheduleOptions{
|
||||
TimeTilDormant: timeTilDormant,
|
||||
TimeTilDormantAutoDelete: 0,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Then: We expect deleting_at to be removed
|
||||
updated, err := db.GetWorkspaceByID(ctx, workspace.ID)
|
||||
require.NoError(t, err)
|
||||
require.False(t, updated.DeletingAt.Valid, "deleting_at should be cleared when auto-deletion is disabled")
|
||||
|
||||
// Then: We expect no notifications to have been sent
|
||||
sent := notifyEnq.Sent()
|
||||
require.Len(t, sent, 0, "no notifications should be sent when disabling dormancy auto-deletion")
|
||||
})
|
||||
}
|
||||
|
||||
func TestTemplateTTL(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user