1
1
package coderd_test
2
2
3
3
import (
4
- "bytes"
5
4
"context"
6
5
"encoding/json"
7
6
"fmt"
7
+ "maps"
8
8
"net"
9
9
"net/http"
10
10
"runtime"
@@ -1937,17 +1937,21 @@ func TestOwnedWorkspacesCoordinate(t *testing.T) {
1937
1937
1938
1938
ctx := testutil .Context (t , testutil .WaitLong )
1939
1939
logger := slogtest .Make (t , nil ).Leveled (slog .LevelDebug )
1940
- firstClient , closer , _ := coderdtest .NewWithAPI (t , & coderdtest.Options {
1940
+ firstClient , closer , api := coderdtest .NewWithAPI (t , & coderdtest.Options {
1941
1941
Coordinator : tailnet .NewCoordinator (logger ),
1942
1942
IncludeProvisionerDaemon : true ,
1943
1943
})
1944
- defer closer .Close ()
1944
+ t .Cleanup (func () {
1945
+ _ = closer .Close ()
1946
+ })
1945
1947
firstUser := coderdtest .CreateFirstUser (t , firstClient )
1946
1948
member , memberUser := coderdtest .CreateAnotherUser (t , firstClient , firstUser .OrganizationID , rbac .RoleTemplateAdmin ())
1947
1949
1948
- // Create a workspace
1949
- token := uuid .NewString ()
1950
- resources , _ := buildWorkspaceWithAgent (t , member , firstUser .OrganizationID , token )
1950
+ // Create a workspace with an agent
1951
+ dbfake .WorkspaceBuild (t , api .Database , database.Workspace {
1952
+ OrganizationID : firstUser .OrganizationID ,
1953
+ OwnerID : memberUser .ID ,
1954
+ }).WithAgent ().Do ()
1951
1955
1952
1956
u , err := member .URL .Parse ("/api/v2/tailnet" )
1953
1957
require .NoError (t , err )
@@ -1990,69 +1994,40 @@ func TestOwnedWorkspacesCoordinate(t *testing.T) {
1990
1994
// Existing agent
1991
1995
require .Len (t , update .UpsertedAgents , 1 )
1992
1996
require .Equal (t , update .UpsertedAgents [0 ].WorkspaceId , wsID )
1993
- require .EqualValues (t , update .UpsertedAgents [0 ].Id , resources [0 ].Agents [0 ].ID )
1994
1997
1995
1998
require .Len (t , update .DeletedWorkspaces , 0 )
1996
1999
require .Len (t , update .DeletedAgents , 0 )
1997
2000
1998
2001
// Build a second workspace
1999
- secondToken := uuid .NewString ()
2000
- secondResources , secondWorkspace := buildWorkspaceWithAgent (t , member , firstUser .OrganizationID , secondToken )
2001
-
2002
- // Workspace starting
2003
- update , err = stream .Recv ()
2004
- require .NoError (t , err )
2005
- require .Len (t , update .UpsertedWorkspaces , 1 )
2006
- require .Equal (t , update .UpsertedWorkspaces [0 ].Status , tailnetproto .Workspace_STARTING )
2007
-
2008
- require .Len (t , update .DeletedWorkspaces , 0 )
2009
- require .Len (t , update .DeletedAgents , 0 )
2010
- require .Len (t , update .UpsertedAgents , 0 )
2011
-
2012
- // Workspace running, agent created
2013
- update , err = stream .Recv ()
2014
- require .NoError (t , err )
2015
- require .Len (t , update .UpsertedWorkspaces , 1 )
2016
- require .Equal (t , update .UpsertedWorkspaces [0 ].Status , tailnetproto .Workspace_RUNNING )
2017
- wsID = update .UpsertedWorkspaces [0 ].Id
2018
- require .Len (t , update .UpsertedAgents , 1 )
2019
- require .Equal (t , update .UpsertedAgents [0 ].WorkspaceId , wsID )
2020
- require .EqualValues (t , update .UpsertedAgents [0 ].Id , secondResources [0 ].Agents [0 ].ID )
2021
-
2022
- require .Len (t , update .DeletedWorkspaces , 0 )
2023
- require .Len (t , update .DeletedAgents , 0 )
2024
-
2025
- _ , err = member .CreateWorkspaceBuild (ctx , secondWorkspace .ID , codersdk.CreateWorkspaceBuildRequest {
2026
- Transition : codersdk .WorkspaceTransitionDelete ,
2027
- })
2028
- require .NoError (t , err )
2029
-
2030
- // Wait for the workspace to be deleted
2031
- deletedAgents := make ([]* tailnetproto.Agent , 0 )
2032
- workspaceUpdates := make ([]* tailnetproto.Workspace , 0 )
2033
- require .Eventually (t , func () bool {
2034
- update , err = stream .Recv ()
2035
- if err != nil {
2036
- return false
2037
- }
2038
- deletedAgents = append (deletedAgents , update .DeletedAgents ... )
2039
- workspaceUpdates = append (workspaceUpdates , update .UpsertedWorkspaces ... )
2040
- return len (update .DeletedWorkspaces ) == 1 &&
2041
- bytes .Equal (update .DeletedWorkspaces [0 ].Id , wsID )
2042
- }, testutil .WaitMedium , testutil .IntervalSlow )
2043
-
2044
- // We should have seen an update for the agent being deleted
2045
- require .Len (t , deletedAgents , 1 )
2046
- require .EqualValues (t , deletedAgents [0 ].Id , secondResources [0 ].Agents [0 ].ID )
2047
-
2048
- // But we may also see a 'pending' state transition before 'deleting'
2049
- deletingFound := false
2050
- for _ , ws := range workspaceUpdates {
2051
- if bytes .Equal (ws .Id , wsID ) && ws .Status == tailnetproto .Workspace_DELETING {
2052
- deletingFound = true
2053
- }
2002
+ secondWorkspace := dbfake .WorkspaceBuild (t , api .Database , database.Workspace {
2003
+ OrganizationID : firstUser .OrganizationID ,
2004
+ OwnerID : memberUser .ID ,
2005
+ }).WithAgent ().Pubsub (api .Pubsub ).Do ()
2006
+
2007
+ // Wait for the second workspace to be running with an agent
2008
+ expectedState := map [uuid.UUID ]workspace {
2009
+ secondWorkspace .Workspace .ID : {
2010
+ Status : tailnetproto .Workspace_RUNNING ,
2011
+ NumAgents : 1 ,
2012
+ },
2054
2013
}
2055
- require .True (t , deletingFound )
2014
+ waitForUpdates (t , ctx , stream , map [uuid.UUID ]workspace {}, expectedState )
2015
+
2016
+ // Wait for the workspace and agent to be deleted
2017
+ secondWorkspace .Workspace .Deleted = true
2018
+ dbfake .WorkspaceBuild (t , api .Database , secondWorkspace .Workspace ).
2019
+ Seed (database.WorkspaceBuild {
2020
+ Transition : database .WorkspaceTransitionDelete ,
2021
+ BuildNumber : 2 ,
2022
+ }).Pubsub (api .Pubsub ).Do ()
2023
+
2024
+ priorState := expectedState
2025
+ waitForUpdates (t , ctx , stream , priorState , map [uuid.UUID ]workspace {
2026
+ secondWorkspace .Workspace .ID : {
2027
+ Status : tailnetproto .Workspace_DELETED ,
2028
+ NumAgents : 0 ,
2029
+ },
2030
+ })
2056
2031
}
2057
2032
2058
2033
func requireGetManifest (ctx context.Context , t testing.TB , aAPI agentproto.DRPCAgentClient ) agentsdk.Manifest {
@@ -2075,17 +2050,90 @@ func postStartup(ctx context.Context, t testing.TB, client agent.Client, startup
2075
2050
return err
2076
2051
}
2077
2052
2078
- func buildWorkspaceWithAgent (t * testing.T , client * codersdk.Client , orgID uuid.UUID , agentToken string ) ([]codersdk.WorkspaceResource , codersdk.Workspace ) {
2079
- version := coderdtest .CreateTemplateVersion (t , client , orgID , & echo.Responses {
2080
- Parse : echo .ParseComplete ,
2081
- ProvisionPlan : echo .PlanComplete ,
2082
- ProvisionApply : echo .ProvisionApplyWithAgent (agentToken ),
2083
- })
2084
- coderdtest .AwaitTemplateVersionJobCompleted (t , client , version .ID )
2085
- template := coderdtest .CreateTemplate (t , client , orgID , version .ID )
2086
- workspace := coderdtest .CreateWorkspace (t , client , template .ID )
2087
- coderdtest .AwaitWorkspaceBuildJobCompleted (t , client , workspace .LatestBuild .ID )
2088
- _ = agenttest .New (t , client .URL , agentToken )
2089
- resources := coderdtest .NewWorkspaceAgentWaiter (t , client , workspace .ID ).Wait ()
2090
- return resources , workspace
2053
+ type workspace struct {
2054
+ Status tailnetproto.Workspace_Status
2055
+ NumAgents int
2056
+ }
2057
+
2058
+ func waitForUpdates (
2059
+ t * testing.T ,
2060
+ //nolint:revive // t takes precedence
2061
+ ctx context.Context ,
2062
+ stream tailnetproto.DRPCTailnet_WorkspaceUpdatesClient ,
2063
+ currentState map [uuid.UUID ]workspace ,
2064
+ expectedState map [uuid.UUID ]workspace ,
2065
+ ) {
2066
+ t .Helper ()
2067
+ errCh := make (chan error , 1 )
2068
+ go func () {
2069
+ for {
2070
+ select {
2071
+ case <- ctx .Done ():
2072
+ errCh <- ctx .Err ()
2073
+ return
2074
+ default :
2075
+ }
2076
+ update , err := stream .Recv ()
2077
+ if err != nil {
2078
+ errCh <- err
2079
+ return
2080
+ }
2081
+ for _ , ws := range update .UpsertedWorkspaces {
2082
+ id , err := uuid .FromBytes (ws .Id )
2083
+ if err != nil {
2084
+ errCh <- err
2085
+ return
2086
+ }
2087
+ currentState [id ] = workspace {
2088
+ Status : ws .Status ,
2089
+ NumAgents : currentState [id ].NumAgents ,
2090
+ }
2091
+ }
2092
+ for _ , ws := range update .DeletedWorkspaces {
2093
+ id , err := uuid .FromBytes (ws .Id )
2094
+ if err != nil {
2095
+ errCh <- err
2096
+ return
2097
+ }
2098
+ currentState [id ] = workspace {
2099
+ Status : tailnetproto .Workspace_DELETED ,
2100
+ NumAgents : currentState [id ].NumAgents ,
2101
+ }
2102
+ }
2103
+ for _ , a := range update .UpsertedAgents {
2104
+ id , err := uuid .FromBytes (a .WorkspaceId )
2105
+ if err != nil {
2106
+ errCh <- err
2107
+ return
2108
+ }
2109
+ currentState [id ] = workspace {
2110
+ Status : currentState [id ].Status ,
2111
+ NumAgents : currentState [id ].NumAgents + 1 ,
2112
+ }
2113
+ }
2114
+ for _ , a := range update .DeletedAgents {
2115
+ id , err := uuid .FromBytes (a .WorkspaceId )
2116
+ if err != nil {
2117
+ errCh <- err
2118
+ return
2119
+ }
2120
+ currentState [id ] = workspace {
2121
+ Status : currentState [id ].Status ,
2122
+ NumAgents : currentState [id ].NumAgents - 1 ,
2123
+ }
2124
+ }
2125
+ if maps .Equal (currentState , expectedState ) {
2126
+ errCh <- nil
2127
+ return
2128
+ }
2129
+ }
2130
+ }()
2131
+ select {
2132
+ case err := <- errCh :
2133
+ if err != nil {
2134
+ t .Fatal (err )
2135
+ }
2136
+ case <- ctx .Done ():
2137
+ t .Fatal ("Timeout waiting for desired state" )
2138
+ }
2091
2139
}
0 commit comments