@@ -16,6 +16,7 @@ import (
16
16
"github.com/coder/coder/v2/coderd/database/dbgen"
17
17
"github.com/coder/coder/v2/coderd/database/dbtestutil"
18
18
agplschedule"github.com/coder/coder/v2/coderd/schedule"
19
+ "github.com/coder/coder/v2/coderd/util/ptr"
19
20
"github.com/coder/coder/v2/cryptorand"
20
21
"github.com/coder/coder/v2/enterprise/coderd/schedule"
21
22
"github.com/coder/coder/v2/testutil"
@@ -27,30 +28,35 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
27
28
db ,_ := dbtestutil .NewDB (t )
28
29
29
30
var (
30
- org = dbgen .Organization (t ,db , database.Organization {})
31
- user = dbgen .User (t ,db , database.User {})
31
+ org = dbgen .Organization (t ,db , database.Organization {})
32
+ quietUser = dbgen .User (t ,db , database.User {
33
+ Username :"quiet" ,
34
+ })
35
+ noQuietUser = dbgen .User (t ,db , database.User {
36
+ Username :"no-quiet" ,
37
+ })
32
38
file = dbgen .File (t ,db , database.File {
33
- CreatedBy :user .ID ,
39
+ CreatedBy :quietUser .ID ,
34
40
})
35
41
templateJob = dbgen .ProvisionerJob (t ,db ,nil , database.ProvisionerJob {
36
42
OrganizationID :org .ID ,
37
43
FileID :file .ID ,
38
- InitiatorID :user .ID ,
44
+ InitiatorID :quietUser .ID ,
39
45
Tags : database.StringMap {
40
46
"foo" :"bar" ,
41
47
},
42
48
})
43
49
templateVersion = dbgen .TemplateVersion (t ,db , database.TemplateVersion {
44
50
OrganizationID :org .ID ,
45
- CreatedBy :user .ID ,
51
+ CreatedBy :quietUser .ID ,
46
52
JobID :templateJob .ID ,
47
53
})
48
54
)
49
55
50
56
const userQuietHoursSchedule = "CRON_TZ=UTC 0 0 * * *" // midnight UTC
51
57
ctx := testutil .Context (t ,testutil .WaitLong )
52
- user ,err := db .UpdateUserQuietHoursSchedule (ctx , database.UpdateUserQuietHoursScheduleParams {
53
- ID :user .ID ,
58
+ quietUser ,err := db .UpdateUserQuietHoursSchedule (ctx , database.UpdateUserQuietHoursScheduleParams {
59
+ ID :quietUser .ID ,
54
60
QuietHoursSchedule :userQuietHoursSchedule ,
55
61
})
56
62
require .NoError (t ,err )
@@ -62,20 +68,23 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
62
68
63
69
// Workspace old max_deadline too soon
64
70
cases := []struct {
65
- name string
66
- now time.Time
67
- deadline time.Time
68
- maxDeadline time.Time
69
- newDeadline time.Time // 0 for no change
71
+ name string
72
+ now time.Time
73
+ deadline time.Time
74
+ maxDeadline time.Time
75
+ // Set to nil for no change.
76
+ newDeadline * time.Time
70
77
newMaxDeadline time.Time
78
+ noQuietHours bool
79
+ autostopReq * agplschedule.TemplateAutostopRequirement
71
80
}{
72
81
{
73
82
name :"SkippedWorkspaceMaxDeadlineTooSoon" ,
74
83
now :buildTime ,
75
84
deadline :buildTime ,
76
85
maxDeadline :buildTime .Add (1 * time .Hour ),
77
86
// Unchanged since the max deadline is too soon.
78
- newDeadline :time. Time {} ,
87
+ newDeadline :nil ,
79
88
newMaxDeadline :buildTime .Add (1 * time .Hour ),
80
89
},
81
90
{
@@ -85,7 +94,7 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
85
94
deadline :buildTime ,
86
95
// Far into the future...
87
96
maxDeadline :nextQuietHours .Add (24 * time .Hour ),
88
- newDeadline :time. Time {} ,
97
+ newDeadline :nil ,
89
98
// We will use now() + 2 hours if the newly calculated max deadline
90
99
// from the workspace build time is before now.
91
100
newMaxDeadline :nextQuietHours .Add (8 * time .Hour ),
@@ -97,7 +106,7 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
97
106
deadline :buildTime ,
98
107
// Far into the future...
99
108
maxDeadline :nextQuietHours .Add (24 * time .Hour ),
100
- newDeadline :time. Time {} ,
109
+ newDeadline :nil ,
101
110
// We will use now() + 2 hours if the newly calculated max deadline
102
111
// from the workspace build time is within the next 2 hours.
103
112
newMaxDeadline :nextQuietHours .Add (1 * time .Hour ),
@@ -109,7 +118,7 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
109
118
deadline :buildTime ,
110
119
// Far into the future...
111
120
maxDeadline :nextQuietHours .Add (24 * time .Hour ),
112
- newDeadline :time. Time {} ,
121
+ newDeadline :nil ,
113
122
newMaxDeadline :nextQuietHours ,
114
123
},
115
124
{
@@ -120,7 +129,56 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
120
129
deadline :nextQuietHours .Add (24 * time .Hour ),
121
130
maxDeadline :nextQuietHours .Add (24 * time .Hour ),
122
131
// The deadline should match since it is after the new max deadline.
123
- newDeadline :nextQuietHours ,
132
+ newDeadline :ptr .Ref (nextQuietHours ),
133
+ newMaxDeadline :nextQuietHours ,
134
+ },
135
+ {
136
+ // There was a bug if a user has no quiet hours set, and autostop
137
+ // req is not turned on, then the max deadline is set to `time.Time{}`.
138
+ // This zero value was "in the past", so the workspace deadline would
139
+ // be set to "now" + 2 hours.
140
+ // This is a mistake because the max deadline being zero means
141
+ // there is no max deadline.
142
+ name :"MaxDeadlineShouldBeUnset" ,
143
+ now :buildTime ,
144
+ deadline :buildTime .Add (time .Hour * 8 ),
145
+ maxDeadline : time.Time {},// No max set
146
+ // Should be unchanged
147
+ newDeadline :ptr .Ref (buildTime .Add (time .Hour * 8 )),
148
+ newMaxDeadline : time.Time {},
149
+ noQuietHours :true ,
150
+ autostopReq :& agplschedule.TemplateAutostopRequirement {
151
+ DaysOfWeek :0 ,
152
+ Weeks :0 ,
153
+ },
154
+ },
155
+ {
156
+ // A bug existed where MaxDeadline could be set, but deadline was
157
+ // `time.Time{}`. This is a logical inconsistency because the "max"
158
+ // deadline was ignored.
159
+ name :"NoDeadline" ,
160
+ now :buildTime ,
161
+ deadline : time.Time {},
162
+ maxDeadline : time.Time {},// No max set
163
+ // Should be unchanged
164
+ newDeadline :ptr .Ref (time.Time {}),
165
+ newMaxDeadline : time.Time {},
166
+ noQuietHours :true ,
167
+ autostopReq :& agplschedule.TemplateAutostopRequirement {
168
+ DaysOfWeek :0 ,
169
+ Weeks :0 ,
170
+ },
171
+ },
172
+
173
+ {
174
+ // Similar to 'NoDeadline' test. This has a MaxDeadline set, so
175
+ // the deadline of the workspace should now be set.
176
+ name :"WorkspaceDeadlineNowSet" ,
177
+ now :nextQuietHours .Add (- 6 * time .Hour ),
178
+ // Start with unset times
179
+ deadline : time.Time {},
180
+ maxDeadline : time.Time {},
181
+ newDeadline :ptr .Ref (nextQuietHours ),
124
182
newMaxDeadline :nextQuietHours ,
125
183
},
126
184
}
@@ -131,6 +189,11 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
131
189
t .Run (c .name ,func (t * testing.T ) {
132
190
t .Parallel ()
133
191
192
+ user := quietUser
193
+ if c .noQuietHours {
194
+ user = noQuietUser
195
+ }
196
+
134
197
t .Log ("buildTime" ,buildTime )
135
198
t .Log ("nextQuietHours" ,nextQuietHours )
136
199
t .Log ("now" ,c .now )
@@ -217,17 +280,22 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
217
280
templateScheduleStore .TimeNowFn = func () time.Time {
218
281
return c .now
219
282
}
283
+
284
+ autostopReq := agplschedule.TemplateAutostopRequirement {
285
+ // Every day
286
+ DaysOfWeek :0b01111111 ,
287
+ Weeks :0 ,
288
+ }
289
+ if c .autostopReq != nil {
290
+ autostopReq = * c .autostopReq
291
+ }
220
292
_ ,err = templateScheduleStore .Set (ctx ,db ,template , agplschedule.TemplateScheduleOptions {
221
- UserAutostartEnabled :false ,
222
- UserAutostopEnabled :false ,
223
- DefaultTTL :0 ,
224
- MaxTTL :0 ,
225
- UseMaxTTL :false ,
226
- AutostopRequirement : agplschedule.TemplateAutostopRequirement {
227
- // Every day
228
- DaysOfWeek :0b01111111 ,
229
- Weeks :0 ,
230
- },
293
+ UserAutostartEnabled :false ,
294
+ UserAutostopEnabled :false ,
295
+ DefaultTTL :0 ,
296
+ MaxTTL :0 ,
297
+ UseMaxTTL :false ,
298
+ AutostopRequirement :autostopReq ,
231
299
FailureTTL :0 ,
232
300
TimeTilDormant :0 ,
233
301
TimeTilDormantAutoDelete :0 ,
@@ -238,10 +306,10 @@ func TestTemplateUpdateBuildDeadlines(t *testing.T) {
238
306
newBuild ,err := db .GetWorkspaceBuildByID (ctx ,wsBuild .ID )
239
307
require .NoError (t ,err )
240
308
241
- if c .newDeadline . IsZero () {
242
- c .newDeadline = wsBuild .Deadline
309
+ if c .newDeadline == nil {
310
+ c .newDeadline = & wsBuild .Deadline
243
311
}
244
- require .WithinDuration (t ,c .newDeadline ,newBuild .Deadline ,time .Second )
312
+ require .WithinDuration (t ,* c .newDeadline ,newBuild .Deadline ,time .Second )
245
313
require .WithinDuration (t ,c .newMaxDeadline ,newBuild .MaxDeadline ,time .Second )
246
314
247
315
// Check that the new build has the same state as before.