1
1
package coderd_test
2
2
3
3
import (
4
+ "bytes"
4
5
"context"
5
6
"database/sql"
6
7
"errors"
@@ -25,6 +26,7 @@ import (
25
26
"github.com/coder/coder/v2/coderd/coderdtest/oidctest"
26
27
"github.com/coder/coder/v2/coderd/database"
27
28
"github.com/coder/coder/v2/coderd/database/dbauthz"
29
+ "github.com/coder/coder/v2/coderd/database/dbfake"
28
30
"github.com/coder/coder/v2/coderd/database/dbgen"
29
31
"github.com/coder/coder/v2/coderd/database/dbtestutil"
30
32
"github.com/coder/coder/v2/coderd/database/dbtime"
@@ -371,42 +373,174 @@ func TestWorkspaceBuildsProvisionerState(t *testing.T) {
371
373
372
374
t .Run ("Orphan" ,func (t * testing.T ) {
373
375
t .Parallel ()
374
- client := coderdtest .New (t ,& coderdtest.Options {IncludeProvisionerDaemon :true })
375
- first := coderdtest .CreateFirstUser (t ,client )
376
-
377
- ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitLong )
378
- defer cancel ()
379
376
380
- version := coderdtest .CreateTemplateVersion (t ,client ,first .OrganizationID ,nil )
381
- template := coderdtest .CreateTemplate (t ,client ,first .OrganizationID ,version .ID )
382
- coderdtest .AwaitTemplateVersionJobCompleted (t ,client ,version .ID )
377
+ t .Run ("WithoutDelete" ,func (t * testing.T ) {
378
+ t .Parallel ()
379
+ client ,store := coderdtest .NewWithDatabase (t ,nil )
380
+ first := coderdtest .CreateFirstUser (t ,client )
381
+ templateAdmin ,templateAdminUser := coderdtest .CreateAnotherUser (t ,client ,first .OrganizationID ,rbac .RoleTemplateAdmin ())
382
+
383
+ r := dbfake .WorkspaceBuild (t ,store , database.WorkspaceTable {
384
+ OwnerID :templateAdminUser .ID ,
385
+ OrganizationID :first .OrganizationID ,
386
+ }).Do ()
387
+
388
+ ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitLong )
389
+ defer cancel ()
390
+
391
+ // Trying to orphan without delete transition fails.
392
+ _ ,err := templateAdmin .CreateWorkspaceBuild (ctx ,r .Workspace .ID , codersdk.CreateWorkspaceBuildRequest {
393
+ TemplateVersionID :r .TemplateVersion .ID ,
394
+ Transition :codersdk .WorkspaceTransitionStart ,
395
+ Orphan :true ,
396
+ })
397
+ require .Error (t ,err ,"Orphan is only permitted when deleting a workspace." )
398
+ cerr := coderdtest .SDKError (t ,err )
399
+ require .Equal (t ,http .StatusBadRequest ,cerr .StatusCode ())
400
+ })
383
401
384
- workspace := coderdtest .CreateWorkspace (t ,client ,template .ID )
385
- coderdtest .AwaitWorkspaceBuildJobCompleted (t ,client ,workspace .LatestBuild .ID )
402
+ t .Run ("WithState" ,func (t * testing.T ) {
403
+ t .Parallel ()
404
+ client ,store := coderdtest .NewWithDatabase (t ,nil )
405
+ first := coderdtest .CreateFirstUser (t ,client )
406
+ templateAdmin ,templateAdminUser := coderdtest .CreateAnotherUser (t ,client ,first .OrganizationID ,rbac .RoleTemplateAdmin ())
407
+
408
+ r := dbfake .WorkspaceBuild (t ,store , database.WorkspaceTable {
409
+ OwnerID :templateAdminUser .ID ,
410
+ OrganizationID :first .OrganizationID ,
411
+ }).Do ()
412
+
413
+ ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitLong )
414
+ defer cancel ()
415
+
416
+ // Providing both state and orphan fails.
417
+ _ ,err := templateAdmin .CreateWorkspaceBuild (ctx ,r .Workspace .ID , codersdk.CreateWorkspaceBuildRequest {
418
+ TemplateVersionID :r .TemplateVersion .ID ,
419
+ Transition :codersdk .WorkspaceTransitionDelete ,
420
+ ProvisionerState : []byte (" " ),
421
+ Orphan :true ,
422
+ })
423
+ require .Error (t ,err )
424
+ cerr := coderdtest .SDKError (t ,err )
425
+ require .Equal (t ,http .StatusBadRequest ,cerr .StatusCode ())
426
+ })
386
427
387
- // Providing both state and orphan fails.
388
- _ ,err := client .CreateWorkspaceBuild (ctx ,workspace .ID , codersdk.CreateWorkspaceBuildRequest {
389
- TemplateVersionID :workspace .LatestBuild .TemplateVersionID ,
390
- Transition :codersdk .WorkspaceTransitionDelete ,
391
- ProvisionerState : []byte (" " ),
392
- Orphan :true ,
428
+ t .Run ("NoPermission" ,func (t * testing.T ) {
429
+ t .Parallel ()
430
+ client ,store := coderdtest .NewWithDatabase (t ,nil )
431
+ first := coderdtest .CreateFirstUser (t ,client )
432
+ member ,memberUser := coderdtest .CreateAnotherUser (t ,client ,first .OrganizationID )
433
+
434
+ r := dbfake .WorkspaceBuild (t ,store , database.WorkspaceTable {
435
+ OwnerID :memberUser .ID ,
436
+ OrganizationID :first .OrganizationID ,
437
+ }).Do ()
438
+
439
+ ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitLong )
440
+ defer cancel ()
441
+
442
+ // Trying to orphan without being a template admin fails.
443
+ _ ,err := member .CreateWorkspaceBuild (ctx ,r .Workspace .ID , codersdk.CreateWorkspaceBuildRequest {
444
+ TemplateVersionID :r .TemplateVersion .ID ,
445
+ Transition :codersdk .WorkspaceTransitionDelete ,
446
+ Orphan :true ,
447
+ })
448
+ require .Error (t ,err )
449
+ cerr := coderdtest .SDKError (t ,err )
450
+ require .Equal (t ,http .StatusForbidden ,cerr .StatusCode ())
393
451
})
394
- require .Error (t ,err )
395
- cerr := coderdtest .SDKError (t ,err )
396
- require .Equal (t ,http .StatusBadRequest ,cerr .StatusCode ())
397
452
398
- // Regular orphan operation succeeds.
399
- build ,err := client .CreateWorkspaceBuild (ctx ,workspace .ID , codersdk.CreateWorkspaceBuildRequest {
400
- TemplateVersionID :workspace .LatestBuild .TemplateVersionID ,
401
- Transition :codersdk .WorkspaceTransitionDelete ,
402
- Orphan :true ,
453
+ t .Run ("OK" ,func (t * testing.T ) {
454
+ // Include a provisioner so that we can test that provisionerdserver
455
+ // performs deletion.
456
+ auditor := audit .NewMock ()
457
+ client ,store := coderdtest .NewWithDatabase (t ,& coderdtest.Options {IncludeProvisionerDaemon :true ,Auditor :auditor })
458
+ first := coderdtest .CreateFirstUser (t ,client )
459
+ templateAdmin ,templateAdminUser := coderdtest .CreateAnotherUser (t ,client ,first .OrganizationID ,rbac .RoleTemplateAdmin ())
460
+
461
+ ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitLong )
462
+ defer cancel ()
463
+ // This is a valid zip file. Without this the job will fail to complete.
464
+ // TODO: add this to dbfake by default.
465
+ zipBytes := make ([]byte ,22 )
466
+ zipBytes [0 ]= 80
467
+ zipBytes [1 ]= 75
468
+ zipBytes [2 ]= 0o5
469
+ zipBytes [3 ]= 0o6
470
+ uploadRes ,err := client .Upload (ctx ,codersdk .ContentTypeZip ,bytes .NewReader (zipBytes ))
471
+ require .NoError (t ,err )
472
+
473
+ tv := dbfake .TemplateVersion (t ,store ).
474
+ FileID (uploadRes .ID ).
475
+ Seed (database.TemplateVersion {
476
+ OrganizationID :first .OrganizationID ,
477
+ CreatedBy :templateAdminUser .ID ,
478
+ }).
479
+ Do ()
480
+
481
+ r := dbfake .WorkspaceBuild (t ,store , database.WorkspaceTable {
482
+ OwnerID :templateAdminUser .ID ,
483
+ OrganizationID :first .OrganizationID ,
484
+ TemplateID :tv .Template .ID ,
485
+ }).Do ()
486
+
487
+ auditor .ResetLogs ()
488
+ // Regular orphan operation succeeds.
489
+ build ,err := templateAdmin .CreateWorkspaceBuild (ctx ,r .Workspace .ID , codersdk.CreateWorkspaceBuildRequest {
490
+ TemplateVersionID :r .TemplateVersion .ID ,
491
+ Transition :codersdk .WorkspaceTransitionDelete ,
492
+ Orphan :true ,
493
+ })
494
+ require .NoError (t ,err )
495
+ coderdtest .AwaitWorkspaceBuildJobCompleted (t ,client ,build .ID )
496
+
497
+ // Validate that the deletion was audited.
498
+ require .True (t ,auditor .Contains (t , database.AuditLog {
499
+ ResourceID :build .ID ,
500
+ Action :database .AuditActionDelete ,
501
+ }))
403
502
})
404
- require .NoError (t ,err )
405
- coderdtest .AwaitWorkspaceBuildJobCompleted (t ,client ,build .ID )
406
503
407
- _ ,err = client .Workspace (ctx ,workspace .ID )
408
- require .Error (t ,err )
409
- require .Equal (t ,http .StatusGone ,coderdtest .SDKError (t ,err ).StatusCode ())
504
+ t .Run ("NoProvisioners" ,func (t * testing.T ) {
505
+ t .Parallel ()
506
+ auditor := audit .NewMock ()
507
+ client ,store := coderdtest .NewWithDatabase (t ,& coderdtest.Options {Auditor :auditor })
508
+ first := coderdtest .CreateFirstUser (t ,client )
509
+ templateAdmin ,templateAdminUser := coderdtest .CreateAnotherUser (t ,client ,first .OrganizationID ,rbac .RoleTemplateAdmin ())
510
+
511
+ ctx ,cancel := context .WithTimeout (context .Background (),testutil .WaitLong )
512
+ defer cancel ()
513
+ r := dbfake .WorkspaceBuild (t ,store , database.WorkspaceTable {
514
+ OwnerID :templateAdminUser .ID ,
515
+ OrganizationID :first .OrganizationID ,
516
+ }).Do ()
517
+
518
+ // nolint:gocritic // For testing
519
+ daemons ,err := store .GetProvisionerDaemons (dbauthz .AsSystemReadProvisionerDaemons (ctx ))
520
+ require .NoError (t ,err )
521
+ require .Empty (t ,daemons ,"Provisioner daemons should be empty for this test" )
522
+
523
+ // Orphan deletion still succeeds despite no provisioners being available.
524
+ build ,err := templateAdmin .CreateWorkspaceBuild (ctx ,r .Workspace .ID , codersdk.CreateWorkspaceBuildRequest {
525
+ TemplateVersionID :r .TemplateVersion .ID ,
526
+ Transition :codersdk .WorkspaceTransitionDelete ,
527
+ Orphan :true ,
528
+ })
529
+ require .NoError (t ,err )
530
+ require .Equal (t ,codersdk .WorkspaceTransitionDelete ,build .Transition )
531
+ require .Equal (t ,codersdk .ProvisionerJobSucceeded ,build .Job .Status )
532
+ require .Empty (t ,build .Job .Error )
533
+
534
+ ws ,err := client .Workspace (ctx ,r .Workspace .ID )
535
+ require .Empty (t ,ws )
536
+ require .Equal (t ,http .StatusGone ,coderdtest .SDKError (t ,err ).StatusCode ())
537
+
538
+ // Validate that the deletion was audited.
539
+ require .True (t ,auditor .Contains (t , database.AuditLog {
540
+ ResourceID :build .ID ,
541
+ Action :database .AuditActionDelete ,
542
+ }))
543
+ })
410
544
})
411
545
}
412
546