11package coderd_test
22
33import (
4+ "bytes"
45 "context"
56 "database/sql"
67 "errors"
@@ -25,6 +26,7 @@ import (
2526 "github.com/coder/coder/v2/coderd/coderdtest/oidctest"
2627 "github.com/coder/coder/v2/coderd/database"
2728 "github.com/coder/coder/v2/coderd/database/dbauthz"
29+ "github.com/coder/coder/v2/coderd/database/dbfake"
2830 "github.com/coder/coder/v2/coderd/database/dbgen"
2931 "github.com/coder/coder/v2/coderd/database/dbtestutil"
3032 "github.com/coder/coder/v2/coderd/database/dbtime"
@@ -371,42 +373,174 @@ func TestWorkspaceBuildsProvisionerState(t *testing.T) {
371373
372374 t .Run ("Orphan" , func (t * testing.T ) {
373375 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 ()
379376
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+ })
383401
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+ })
386427
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 ())
393451 })
394- require .Error (t , err )
395- cerr := coderdtest .SDKError (t , err )
396- require .Equal (t , http .StatusBadRequest , cerr .StatusCode ())
397452
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+ }))
403502 })
404- require .NoError (t , err )
405- coderdtest .AwaitWorkspaceBuildJobCompleted (t , client , build .ID )
406503
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+ })
410544 })
411545}
412546
0 commit comments