Skip to content

Commit f6891bc

Browse files
authored
fix: implement fake DeleteOldWorkspaceAgentLogs (#11042)
1 parent 088fd0b commit f6891bc

File tree

2 files changed

+141
-5
lines changed

2 files changed

+141
-5
lines changed

coderd/database/dbmem/dbmem.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,8 +1154,29 @@ func (q *FakeQuerier) DeleteOldProvisionerDaemons(_ context.Context) error {
11541154
return nil
11551155
}
11561156

1157-
func (*FakeQuerier) DeleteOldWorkspaceAgentLogs(_ context.Context) error {
1158-
// no-op
1157+
func (q *FakeQuerier) DeleteOldWorkspaceAgentLogs(_ context.Context) error {
1158+
q.mutex.Lock()
1159+
defer q.mutex.Unlock()
1160+
1161+
now := dbtime.Now()
1162+
weekInterval := 7 * 24 * time.Hour
1163+
weekAgo := now.Add(-weekInterval)
1164+
1165+
var validLogs []database.WorkspaceAgentLog
1166+
for _, log := range q.workspaceAgentLogs {
1167+
var toBeDeleted bool
1168+
for _, agent := range q.workspaceAgents {
1169+
if agent.ID == log.AgentID && agent.LastConnectedAt.Valid && agent.LastConnectedAt.Time.Before(weekAgo) {
1170+
toBeDeleted = true
1171+
break
1172+
}
1173+
}
1174+
1175+
if !toBeDeleted {
1176+
validLogs = append(validLogs, log)
1177+
}
1178+
}
1179+
q.workspaceAgentLogs = validLogs
11591180
return nil
11601181
}
11611182

coderd/database/dbpurge/dbpurge_test.go

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"cdr.dev/slog/sloggers/slogtest"
1515

1616
"github.com/coder/coder/v2/coderd/database"
17+
"github.com/coder/coder/v2/coderd/database/dbgen"
1718
"github.com/coder/coder/v2/coderd/database/dbmem"
1819
"github.com/coder/coder/v2/coderd/database/dbpurge"
1920
"github.com/coder/coder/v2/coderd/database/dbtestutil"
@@ -33,6 +34,118 @@ func TestPurge(t *testing.T) {
3334
require.NoError(t, err)
3435
}
3536

37+
func TestDeleteOldWorkspaceAgentLogs(t *testing.T) {
38+
t.Parallel()
39+
40+
db, _ := dbtestutil.NewDB(t)
41+
org := dbgen.Organization(t, db, database.Organization{})
42+
user := dbgen.User(t, db, database.User{})
43+
_ = dbgen.OrganizationMember(t, db, database.OrganizationMember{UserID: user.ID, OrganizationID: org.ID})
44+
tv := dbgen.TemplateVersion(t, db, database.TemplateVersion{OrganizationID: org.ID, CreatedBy: user.ID})
45+
tmpl := dbgen.Template(t, db, database.Template{OrganizationID: org.ID, ActiveVersionID: tv.ID, CreatedBy: user.ID})
46+
47+
logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true})
48+
now := dbtime.Now()
49+
50+
t.Run("AgentHasNotConnectedSinceWeek_LogsExpired", func(t *testing.T) {
51+
t.Parallel()
52+
53+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
54+
defer cancel()
55+
56+
// given
57+
agent := mustCreateAgentWithLogs(ctx, t, db, user, org, tmpl, tv, now.Add(-8*24*time.Hour), t.Name())
58+
59+
// when
60+
closer := dbpurge.New(ctx, logger, db)
61+
defer closer.Close()
62+
63+
// then
64+
require.Eventually(t, func() bool {
65+
agentLogs, err := db.GetWorkspaceAgentLogsAfter(ctx, database.GetWorkspaceAgentLogsAfterParams{
66+
AgentID: agent,
67+
})
68+
if err != nil {
69+
return false
70+
}
71+
return !containsAgentLog(agentLogs, t.Name())
72+
}, testutil.WaitShort, testutil.IntervalFast)
73+
})
74+
75+
t.Run("AgentConnectedSixDaysAgo_LogsValid", func(t *testing.T) {
76+
t.Parallel()
77+
78+
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitShort)
79+
defer cancel()
80+
81+
// given
82+
agent := mustCreateAgentWithLogs(ctx, t, db, user, org, tmpl, tv, now.Add(-6*24*time.Hour), t.Name())
83+
84+
// when
85+
closer := dbpurge.New(ctx, logger, db)
86+
defer closer.Close()
87+
88+
// then
89+
require.Eventually(t, func() bool {
90+
agentLogs, err := db.GetWorkspaceAgentLogsAfter(ctx, database.GetWorkspaceAgentLogsAfterParams{
91+
AgentID: agent,
92+
})
93+
if err != nil {
94+
return false
95+
}
96+
return containsAgentLog(agentLogs, t.Name())
97+
}, testutil.WaitShort, testutil.IntervalFast)
98+
})
99+
}
100+
101+
func mustCreateAgentWithLogs(ctx context.Context, t *testing.T, db database.Store, user database.User, org database.Organization, tmpl database.Template, tv database.TemplateVersion, agentLastConnectedAt time.Time, output string) uuid.UUID {
102+
agent := mustCreateAgent(t, db, user, org, tmpl, tv)
103+
104+
err := db.UpdateWorkspaceAgentConnectionByID(ctx, database.UpdateWorkspaceAgentConnectionByIDParams{
105+
ID: agent.ID,
106+
LastConnectedAt: sql.NullTime{Time: agentLastConnectedAt, Valid: true},
107+
})
108+
require.NoError(t, err)
109+
_, err = db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{
110+
AgentID: agent.ID,
111+
CreatedAt: agentLastConnectedAt,
112+
Output: []string{output},
113+
Level: []database.LogLevel{database.LogLevelDebug},
114+
})
115+
require.NoError(t, err)
116+
return agent.ID
117+
}
118+
119+
func mustCreateAgent(t *testing.T, db database.Store, user database.User, org database.Organization, tmpl database.Template, tv database.TemplateVersion) database.WorkspaceAgent {
120+
workspace := dbgen.Workspace(t, db, database.Workspace{OwnerID: user.ID, OrganizationID: org.ID, TemplateID: tmpl.ID})
121+
job := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
122+
OrganizationID: org.ID,
123+
Type: database.ProvisionerJobTypeWorkspaceBuild,
124+
Provisioner: database.ProvisionerTypeEcho,
125+
StorageMethod: database.ProvisionerStorageMethodFile,
126+
})
127+
_ = dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
128+
WorkspaceID: workspace.ID,
129+
JobID: job.ID,
130+
TemplateVersionID: tv.ID,
131+
Transition: database.WorkspaceTransitionStart,
132+
Reason: database.BuildReasonInitiator,
133+
})
134+
resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{
135+
JobID: job.ID,
136+
Transition: database.WorkspaceTransitionStart,
137+
})
138+
return dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{
139+
ResourceID: resource.ID,
140+
})
141+
}
142+
143+
func containsAgentLog(daemons []database.WorkspaceAgentLog, output string) bool {
144+
return slices.ContainsFunc(daemons, func(d database.WorkspaceAgentLog) bool {
145+
return d.Output == output
146+
})
147+
}
148+
36149
func TestDeleteOldProvisionerDaemons(t *testing.T) {
37150
t.Parallel()
38151

@@ -91,12 +204,14 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
91204
if err != nil {
92205
return false
93206
}
94-
return contains(daemons, "external-0") &&
95-
contains(daemons, "external-3")
207+
return containsProvisionerDaemon(daemons, "external-0") &&
208+
!containsProvisionerDaemon(daemons, "external-1") &&
209+
!containsProvisionerDaemon(daemons, "external-2") &&
210+
containsProvisionerDaemon(daemons, "external-3")
96211
}, testutil.WaitShort, testutil.IntervalFast)
97212
}
98213

99-
func contains(daemons []database.ProvisionerDaemon, name string) bool {
214+
func containsProvisionerDaemon(daemons []database.ProvisionerDaemon, name string) bool {
100215
return slices.ContainsFunc(daemons, func(d database.ProvisionerDaemon) bool {
101216
return d.Name == name
102217
})

0 commit comments

Comments
 (0)