@@ -1861,6 +1861,91 @@ func TestWorkspaceAgentExternalAuthListen(t *testing.T) {
18611861})
18621862}
18631863
1864+ func TestOwnedWorkspacesCoordinate (t * testing.T ) {
1865+ t .Parallel ()
1866+
1867+ ctx := testutil .Context (t ,testutil .WaitLong )
1868+ logger := slogtest .Make (t ,nil ).Leveled (slog .LevelDebug )
1869+ firstClient ,closer ,_ := coderdtest .NewWithAPI (t ,& coderdtest.Options {
1870+ Coordinator :tailnet .NewCoordinator (logger ),
1871+ IncludeProvisionerDaemon :true ,
1872+ })
1873+ defer closer .Close ()
1874+ firstUser := coderdtest .CreateFirstUser (t ,firstClient )
1875+ user ,_ := coderdtest .CreateAnotherUser (t ,firstClient ,firstUser .OrganizationID )
1876+
1877+ u ,err := user .URL .Parse ("/api/v2/users/me/tailnet" )
1878+ require .NoError (t ,err )
1879+ q := u .Query ()
1880+ q .Set ("version" ,"2.0" )
1881+ u .RawQuery = q .Encode ()
1882+
1883+ //nolint:bodyclose // websocket package closes this for you
1884+ wsConn ,resp ,err := websocket .Dial (ctx ,u .String (),& websocket.DialOptions {
1885+ HTTPHeader : http.Header {
1886+ "Coder-Session-Token" : []string {user .SessionToken ()},
1887+ },
1888+ })
1889+ if err != nil {
1890+ if resp .StatusCode != http .StatusSwitchingProtocols {
1891+ err = codersdk .ReadBodyAsError (resp )
1892+ }
1893+ require .NoError (t ,err )
1894+ }
1895+ defer wsConn .Close (websocket .StatusNormalClosure ,"done" )
1896+
1897+ rpcClient ,err := tailnet .NewDRPCClient (
1898+ websocket .NetConn (ctx ,wsConn ,websocket .MessageBinary ),
1899+ logger ,
1900+ )
1901+ require .NoError (t ,err )
1902+
1903+ stream ,err := rpcClient .WorkspaceUpdates (ctx ,& tailnetproto.WorkspaceUpdatesRequest {})
1904+ require .NoError (t ,err )
1905+
1906+ update ,err := stream .Recv ()
1907+ require .NoError (t ,err )
1908+ require .Len (t ,update .UpsertedWorkspaces ,0 )
1909+ require .Len (t ,update .DeletedWorkspaces ,0 )
1910+ require .Len (t ,update .UpsertedAgents ,0 )
1911+ require .Len (t ,update .DeletedAgents ,0 )
1912+
1913+ // Build a workspace
1914+ authToken := uuid .NewString ()
1915+ version := coderdtest .CreateTemplateVersion (t ,firstClient ,firstUser .OrganizationID ,& echo.Responses {
1916+ Parse :echo .ParseComplete ,
1917+ ProvisionPlan :echo .PlanComplete ,
1918+ ProvisionApply :echo .ProvisionApplyWithAgent (authToken ),
1919+ })
1920+ coderdtest .AwaitTemplateVersionJobCompleted (t ,firstClient ,version .ID )
1921+ template := coderdtest .CreateTemplate (t ,firstClient ,firstUser .OrganizationID ,version .ID )
1922+ workspace := coderdtest .CreateWorkspace (t ,user ,template .ID )
1923+ coderdtest .AwaitWorkspaceBuildJobCompleted (t ,firstClient ,workspace .LatestBuild .ID )
1924+
1925+ // Starting workspace
1926+ update ,err = stream .Recv ()
1927+ require .NoError (t ,err )
1928+ require .Len (t ,update .UpsertedWorkspaces ,1 )
1929+ require .Equal (t ,update .UpsertedWorkspaces [0 ].Status ,tailnetproto .Workspace_STARTING )
1930+
1931+ // Workspace is running
1932+ update ,err = stream .Recv ()
1933+ require .NoError (t ,err )
1934+ require .Len (t ,update .UpsertedWorkspaces ,1 )
1935+ require .Equal (t ,update .UpsertedWorkspaces [0 ].Status ,tailnetproto .Workspace_RUNNING )
1936+ wsID := update .UpsertedWorkspaces [0 ].Id
1937+
1938+ _ = agenttest .New (t ,user .URL ,authToken )
1939+ resources := coderdtest .NewWorkspaceAgentWaiter (t ,user ,workspace .ID ).Wait ()
1940+
1941+ // Agent is created
1942+ update ,err = stream .Recv ()
1943+ require .NoError (t ,err )
1944+ require .Len (t ,update .UpsertedAgents ,1 )
1945+ require .Equal (t ,update .UpsertedAgents [0 ].WorkspaceId ,wsID )
1946+ require .EqualValues (t ,update .UpsertedAgents [0 ].Id ,resources [0 ].Agents [0 ].ID )
1947+ }
1948+
18641949func requireGetManifest (ctx context.Context ,t testing.TB ,aAPI agentproto.DRPCAgentClient ) agentsdk.Manifest {
18651950mp ,err := aAPI .GetManifest (ctx ,& agentproto.GetManifestRequest {})
18661951require .NoError (t ,err )