@@ -2,7 +2,7 @@ package executor
2
2
3
3
import (
4
4
"context"
5
- "encoding/json "
5
+ "database/sql "
6
6
"sync/atomic"
7
7
"time"
8
8
@@ -13,8 +13,8 @@ import (
13
13
"cdr.dev/slog"
14
14
"github.com/coder/coder/coderd/database"
15
15
"github.com/coder/coder/coderd/database/dbauthz"
16
- "github.com/coder/coder/coderd/provisionerdserver"
17
16
"github.com/coder/coder/coderd/schedule"
17
+ "github.com/coder/coder/coderd/wsbuilder"
18
18
)
19
19
20
20
// Executor automatically starts or stops workspaces.
@@ -168,20 +168,35 @@ func (e *Executor) runOnce(t time.Time) Stats {
168
168
)
169
169
return nil
170
170
}
171
-
172
- log .Info (e .ctx , "scheduling workspace transition" , slog .F ("transition" , validTransition ))
173
-
174
- stats .Transitions [ws .ID ] = validTransition
175
- if err := build (e .ctx , db , ws , validTransition , priorHistory , priorJob ); err != nil {
171
+ builder := wsbuilder .New (ws , validTransition ).
172
+ SetLastWorkspaceBuildInTx (& priorHistory ).
173
+ SetLastWorkspaceBuildJobInTx (& priorJob )
174
+
175
+ switch validTransition {
176
+ case database .WorkspaceTransitionStart :
177
+ builder = builder .Reason (database .BuildReasonAutostart )
178
+ case database .WorkspaceTransitionStop :
179
+ builder = builder .Reason (database .BuildReasonAutostop )
180
+ default :
181
+ log .Error (e .ctx , "unsupported transition" , slog .F ("transition" , validTransition ))
182
+ return nil
183
+ }
184
+ if _ , _ , err := builder .Build (e .ctx , db , nil ); err != nil {
176
185
log .Error (e .ctx , "unable to transition workspace" ,
177
186
slog .F ("transition" , validTransition ),
178
187
slog .Error (err ),
179
188
)
180
189
return nil
181
190
}
191
+ stats .Transitions [ws .ID ] = validTransition
192
+
193
+ log .Info (e .ctx , "scheduling workspace transition" , slog .F ("transition" , validTransition ))
182
194
183
195
return nil
184
- }, nil )
196
+
197
+ // Run with RepeatableRead isolation so that the build process sees the same data
198
+ // as our calculation that determines whether an autobuild is necessary.
199
+ }, & sql.TxOptions {Isolation : sql .LevelRepeatableRead })
185
200
if err != nil {
186
201
log .Error (e .ctx , "workspace scheduling failed" , slog .Error (err ))
187
202
}
@@ -248,92 +263,3 @@ func getNextTransition(
248
263
return "" , time.Time {}, xerrors .Errorf ("last transition not valid for autostart or autostop" )
249
264
}
250
265
}
251
-
252
- // TODO(cian): this function duplicates most of api.postWorkspaceBuilds. Refactor.
253
- // See: https://github.com/coder/coder/issues/1401
254
- func build (ctx context.Context , store database.Store , workspace database.Workspace , trans database.WorkspaceTransition , priorHistory database.WorkspaceBuild , priorJob database.ProvisionerJob ) error {
255
- template , err := store .GetTemplateByID (ctx , workspace .TemplateID )
256
- if err != nil {
257
- return xerrors .Errorf ("get workspace template: %w" , err )
258
- }
259
-
260
- priorBuildNumber := priorHistory .BuildNumber
261
-
262
- // This must happen in a transaction to ensure history can be inserted, and
263
- // the prior history can update it's "after" column to point at the new.
264
- workspaceBuildID := uuid .New ()
265
- input , err := json .Marshal (provisionerdserver.WorkspaceProvisionJob {
266
- WorkspaceBuildID : workspaceBuildID ,
267
- })
268
- if err != nil {
269
- return xerrors .Errorf ("marshal provision job: %w" , err )
270
- }
271
- provisionerJobID := uuid .New ()
272
- now := database .Now ()
273
-
274
- var buildReason database.BuildReason
275
- switch trans {
276
- case database .WorkspaceTransitionStart :
277
- buildReason = database .BuildReasonAutostart
278
- case database .WorkspaceTransitionStop :
279
- buildReason = database .BuildReasonAutostop
280
- default :
281
- return xerrors .Errorf ("Unsupported transition: %q" , trans )
282
- }
283
-
284
- lastBuildParameters , err := store .GetWorkspaceBuildParameters (ctx , priorHistory .ID )
285
- if err != nil {
286
- return xerrors .Errorf ("fetch prior workspace build parameters: %w" , err )
287
- }
288
-
289
- return store .InTx (func (db database.Store ) error {
290
- newProvisionerJob , err := store .InsertProvisionerJob (ctx , database.InsertProvisionerJobParams {
291
- ID : provisionerJobID ,
292
- CreatedAt : now ,
293
- UpdatedAt : now ,
294
- InitiatorID : workspace .OwnerID ,
295
- OrganizationID : template .OrganizationID ,
296
- Provisioner : template .Provisioner ,
297
- Type : database .ProvisionerJobTypeWorkspaceBuild ,
298
- StorageMethod : priorJob .StorageMethod ,
299
- FileID : priorJob .FileID ,
300
- Tags : priorJob .Tags ,
301
- Input : input ,
302
- })
303
- if err != nil {
304
- return xerrors .Errorf ("insert provisioner job: %w" , err )
305
- }
306
- workspaceBuild , err := store .InsertWorkspaceBuild (ctx , database.InsertWorkspaceBuildParams {
307
- ID : workspaceBuildID ,
308
- CreatedAt : now ,
309
- UpdatedAt : now ,
310
- WorkspaceID : workspace .ID ,
311
- TemplateVersionID : priorHistory .TemplateVersionID ,
312
- BuildNumber : priorBuildNumber + 1 ,
313
- ProvisionerState : priorHistory .ProvisionerState ,
314
- InitiatorID : workspace .OwnerID ,
315
- Transition : trans ,
316
- JobID : newProvisionerJob .ID ,
317
- Reason : buildReason ,
318
- })
319
- if err != nil {
320
- return xerrors .Errorf ("insert workspace build: %w" , err )
321
- }
322
-
323
- names := make ([]string , 0 , len (lastBuildParameters ))
324
- values := make ([]string , 0 , len (lastBuildParameters ))
325
- for _ , param := range lastBuildParameters {
326
- names = append (names , param .Name )
327
- values = append (values , param .Value )
328
- }
329
- err = db .InsertWorkspaceBuildParameters (ctx , database.InsertWorkspaceBuildParametersParams {
330
- WorkspaceBuildID : workspaceBuild .ID ,
331
- Name : names ,
332
- Value : values ,
333
- })
334
- if err != nil {
335
- return xerrors .Errorf ("insert workspace build parameters: %w" , err )
336
- }
337
- return nil
338
- }, nil )
339
- }
0 commit comments