Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0226fdf

Browse files
committed
Add notification for marked as deletion
1 parent 48ad269 commit 0226fdf

File tree

7 files changed

+158
-12
lines changed

7 files changed

+158
-12
lines changed

coderd/database/dbmem/dbmem.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8709,6 +8709,7 @@ func (q *FakeQuerier) UpdateWorkspacesDormantDeletingAtByTemplateID(_ context.Co
87098709
return nil, err
87108710
}
87118711

8712+
affectedRows := []database.Workspace{}
87128713
for i, ws := range q.workspaces {
87138714
if ws.TemplateID != arg.TemplateID {
87148715
continue
@@ -8733,9 +8734,10 @@ func (q *FakeQuerier) UpdateWorkspacesDormantDeletingAtByTemplateID(_ context.Co
87338734
}
87348735
ws.DeletingAt = deletingAt
87358736
q.workspaces[i] = ws
8737+
affectedRows = append(affectedRows, ws)
87368738
}
87378739

8738-
return q.workspaces, nil
8740+
return affectedRows, nil
87398741
}
87408742

87418743
func (q *FakeQuerier) UpsertAnnouncementBanners(_ context.Context, data string) error {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
DELETE FROM notification_templates
2+
WHERE
3+
id = '51ce2fdf-c9ca-4be1-8d70-628674f9bc42';
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
INSERT INTO
2+
notification_templates (
3+
id,
4+
name,
5+
title_template,
6+
body_template,
7+
"group",
8+
actions
9+
)
10+
VALUES (
11+
'51ce2fdf-c9ca-4be1-8d70-628674f9bc42',
12+
'Workspace Marked for Deletion',
13+
E'Workspace "{{.Labels.name}}" marked for deletion',
14+
E'Hi {{.UserName}}\n\n' || E'Your workspace **{{.Labels.name}}** has been marked for **deletion** after {{.Labels.dormancyHours}} hours of dormancy.\n' || E'The specified reason was "**{{.Labels.reason}} (initiated by: {{ .Labels.initiator }}){{end}}**\n\n' || E'Dormancy refers to a workspace being unused for a defined length of time, and after it exceeds {{.Labels.dormancyHours}} hours of dormancy it will be deleted.\n' || E'To prevent your workspace from being deleted, simply use it as normal.',
15+
'Workspace Events',
16+
'[
17+
{
18+
"label": "View workspace",
19+
"url": "{{ base_url }}/@{{.UserName}}/{{.Labels.name}}"
20+
}
21+
]'::jsonb
22+
);

coderd/dormancy/notifications.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,33 @@ func NotifyWorkspaceDormant(
4444
notification.Workspace.OrganizationID,
4545
)
4646
}
47+
48+
type WorkspaceMarkedForDeletionNotification struct {
49+
Workspace database.Workspace
50+
Reason string
51+
CreatedBy string
52+
}
53+
54+
func NotifyWorkspaceMarkedForDeletion(
55+
ctx context.Context,
56+
enqueuer notifications.Enqueuer,
57+
notification WorkspaceMarkedForDeletionNotification,
58+
) (id *uuid.UUID, err error) {
59+
labels := map[string]string{
60+
"name": notification.Workspace.Name,
61+
"initiator": "autobuild",
62+
"reason": notification.Reason,
63+
}
64+
return enqueuer.Enqueue(
65+
ctx,
66+
notification.Workspace.OwnerID,
67+
notifications.TemplateWorkspaceDormant,
68+
labels,
69+
notification.CreatedBy,
70+
// Associate this notification with all the related entities.
71+
notification.Workspace.ID,
72+
notification.Workspace.OwnerID,
73+
notification.Workspace.TemplateID,
74+
notification.Workspace.OrganizationID,
75+
)
76+
}

coderd/notifications/events.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ import "github.com/google/uuid"
77

88
// Workspace-related events.
99
var (
10-
TemplateWorkspaceDeleted = uuid.MustParse("f517da0b-cdc9-410f-ab89-a86107c420ed")
11-
WorkspaceAutobuildFailed = uuid.MustParse("381df2a9-c0c0-4749-420f-80a9280c66f9")
12-
TemplateWorkspaceDormant = uuid.MustParse("0ea69165-ec14-4314-91f1-69566ac3c5a0")
13-
WorkspaceAutoUpdated = uuid.MustParse("c34a0c09-0704-4cac-bd1c-0c0146811c2b")
10+
TemplateWorkspaceDeleted = uuid.MustParse("f517da0b-cdc9-410f-ab89-a86107c420ed")
11+
WorkspaceAutobuildFailed = uuid.MustParse("381df2a9-c0c0-4749-420f-80a9280c66f9")
12+
TemplateWorkspaceDormant = uuid.MustParse("0ea69165-ec14-4314-91f1-69566ac3c5a0")
13+
WorkspaceAutoUpdated = uuid.MustParse("c34a0c09-0704-4cac-bd1c-0c0146811c2b")
14+
TemplateWorkspaceMarkedForDeletion = uuid.MustParse("51ce2fdf-c9ca-4be1-8d70-628674f9bc42")
1415
)

enterprise/coderd/schedule/template.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
136136

137137
var (
138138
template database.Template
139-
dormantWorkspaces []database.Workspace
139+
markedForDeletion []database.Workspace
140140
)
141141
err = db.InTx(func(tx database.Store) error {
142142
ctx, span := tracing.StartSpanWithName(ctx, "(*schedule.EnterpriseTemplateScheduleStore).Set()-InTx()")
@@ -171,7 +171,7 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
171171
// to ensure workspaces are being cleaned up correctly. Similarly if we are
172172
// disabling it (by passing 0), then we want to delete nullify the deleting_at
173173
// fields of all the template workspaces.
174-
dormantWorkspaces, err = tx.UpdateWorkspacesDormantDeletingAtByTemplateID(ctx, database.UpdateWorkspacesDormantDeletingAtByTemplateIDParams{
174+
markedForDeletion, err = tx.UpdateWorkspacesDormantDeletingAtByTemplateID(ctx, database.UpdateWorkspacesDormantDeletingAtByTemplateIDParams{
175175
TemplateID: tpl.ID,
176176
TimeTilDormantAutodeleteMs: opts.TimeTilDormantAutoDelete.Milliseconds(),
177177
DormantAt: dormantAt,
@@ -205,19 +205,18 @@ func (s *EnterpriseTemplateScheduleStore) Set(ctx context.Context, db database.S
205205
return database.Template{}, err
206206
}
207207

208-
for _, workspace := range dormantWorkspaces {
209-
_, err = dormancy.NotifyWorkspaceDormant(
208+
for _, workspace := range markedForDeletion {
209+
_, err = dormancy.NotifyWorkspaceMarkedForDeletion(
210210
ctx,
211211
s.enqueuer,
212-
dormancy.WorkspaceDormantNotification{
212+
dormancy.WorkspaceMarkedForDeletionNotification{
213213
Workspace: workspace,
214-
Initiator: "autobuild",
215214
Reason: "template updated to new dormancy policy",
216215
CreatedBy: "scheduletemplate",
217216
},
218217
)
219218
if err != nil {
220-
s.logger.Warn(ctx, "failed to notify of workspace marked as dormant", slog.Error(err))
219+
s.logger.Warn(ctx, "failed to notify of workspace marked for deletion", slog.Error(err))
221220
}
222221
}
223222

enterprise/coderd/schedule/template_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,95 @@ func TestTemplateUpdateBuildDeadlinesSkip(t *testing.T) {
606606
}
607607
}
608608

609+
func TestNotifications(t *testing.T) {
610+
t.Parallel()
611+
612+
t.Run("Dormancy", func(t *testing.T) {
613+
t.Parallel()
614+
615+
var (
616+
db, _ = dbtestutil.NewDB(t)
617+
ctx = testutil.Context(t, testutil.WaitLong)
618+
user = dbgen.User(t, db, database.User{})
619+
file = dbgen.File(t, db, database.File{
620+
CreatedBy: user.ID,
621+
})
622+
templateJob = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
623+
FileID: file.ID,
624+
InitiatorID: user.ID,
625+
Tags: database.StringMap{
626+
"foo": "bar",
627+
},
628+
})
629+
timeTilDormant = time.Minute * 2
630+
templateVersion = dbgen.TemplateVersion(t, db, database.TemplateVersion{
631+
CreatedBy: user.ID,
632+
JobID: templateJob.ID,
633+
OrganizationID: templateJob.OrganizationID,
634+
})
635+
template = dbgen.Template(t, db, database.Template{
636+
ActiveVersionID: templateVersion.ID,
637+
CreatedBy: user.ID,
638+
OrganizationID: templateJob.OrganizationID,
639+
TimeTilDormant: int64(timeTilDormant),
640+
TimeTilDormantAutoDelete: int64(timeTilDormant),
641+
})
642+
)
643+
644+
// Add two dormant workspaces and one active workspace.
645+
dormantWorkspaces := []database.Workspace{
646+
dbgen.Workspace(t, db, database.Workspace{
647+
OwnerID: user.ID,
648+
TemplateID: template.ID,
649+
OrganizationID: templateJob.OrganizationID,
650+
LastUsedAt: time.Now().Add(-time.Hour),
651+
}),
652+
dbgen.Workspace(t, db, database.Workspace{
653+
OwnerID: user.ID,
654+
TemplateID: template.ID,
655+
OrganizationID: templateJob.OrganizationID,
656+
LastUsedAt: time.Now().Add(-time.Hour),
657+
}),
658+
}
659+
dbgen.Workspace(t, db, database.Workspace{
660+
OwnerID: user.ID,
661+
TemplateID: template.ID,
662+
OrganizationID: templateJob.OrganizationID,
663+
LastUsedAt: time.Now(),
664+
})
665+
for _, ws := range dormantWorkspaces {
666+
db.UpdateWorkspaceDormantDeletingAt(ctx, database.UpdateWorkspaceDormantDeletingAtParams{
667+
ID: ws.ID,
668+
DormantAt: sql.NullTime{
669+
Time: ws.LastUsedAt.Add(timeTilDormant),
670+
Valid: true,
671+
},
672+
})
673+
}
674+
675+
// Setup dependencies
676+
notifyEnq := testutil.FakeNotificationsEnqueuer{}
677+
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug)
678+
const userQuietHoursSchedule = "CRON_TZ=UTC 0 0 * * *" // midnight UTC
679+
userQuietHoursStore, err := schedule.NewEnterpriseUserQuietHoursScheduleStore(userQuietHoursSchedule, true)
680+
require.NoError(t, err)
681+
userQuietHoursStorePtr := &atomic.Pointer[agplschedule.UserQuietHoursScheduleStore]{}
682+
userQuietHoursStorePtr.Store(&userQuietHoursStore)
683+
templateScheduleStore := schedule.NewEnterpriseTemplateScheduleStore(userQuietHoursStorePtr, &notifyEnq, logger)
684+
templateScheduleStore.TimeNowFn = time.Now
685+
686+
// Update dormancy TTL for a lower value
687+
_, err = templateScheduleStore.Set(ctx, db, template, agplschedule.TemplateScheduleOptions{
688+
TimeTilDormant: timeTilDormant / 2,
689+
TimeTilDormantAutoDelete: timeTilDormant / 2,
690+
})
691+
require.NoError(t, err)
692+
693+
// We should expect two notifications. One for each dormant workspace.
694+
require.Len(t, notifyEnq.Sent, len(dormantWorkspaces))
695+
})
696+
}
697+
609698
func must[V any](v V, err error) V {
610699
if err != nil {
611700
panic(err)

0 commit comments

Comments
 (0)