@@ -40,7 +40,6 @@ import {
40
40
Workspace ,
41
41
WorkspaceContext ,
42
42
WorkspaceCreationResult ,
43
- WorkspaceImageBuild ,
44
43
WorkspaceInfo ,
45
44
WorkspaceInstance ,
46
45
WorkspaceInstancePort ,
@@ -97,7 +96,6 @@ import { WorkspaceManagerClientProvider } from "@gitpod/ws-manager/lib/client-pr
97
96
import {
98
97
AdmissionLevel ,
99
98
ControlAdmissionRequest ,
100
- MarkActiveRequest ,
101
99
StopWorkspacePolicy ,
102
100
TakeSnapshotRequest ,
103
101
} from "@gitpod/ws-manager/lib/core_pb" ;
@@ -120,7 +118,6 @@ import { ContextParser } from "./context-parser-service";
120
118
import { GitTokenScopeGuesser } from "./git-token-scope-guesser" ;
121
119
import { isClusterMaintenanceError } from "./workspace-starter" ;
122
120
import { HeadlessLogUrls } from "@gitpod/gitpod-protocol/lib/headless-workspace-log" ;
123
- import { HeadlessLogService , HeadlessLogEndpoint } from "./headless-log-service" ;
124
121
import { ConfigProvider , InvalidGitpodYMLError } from "./config-provider" ;
125
122
import { ProjectsService } from "../projects/projects-service" ;
126
123
import { IDEOptions } from "@gitpod/gitpod-protocol/lib/ide-protocol" ;
@@ -140,7 +137,6 @@ import {
140
137
UserFeatureSettings ,
141
138
WorkspaceTimeoutSetting ,
142
139
} from "@gitpod/gitpod-protocol/lib/protocol" ;
143
- import { Deferred } from "@gitpod/gitpod-protocol/lib/util/deferred" ;
144
140
import { ListUsageRequest , ListUsageResponse } from "@gitpod/gitpod-protocol/lib/usage" ;
145
141
import { VerificationService } from "../auth/verification-service" ;
146
142
import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode" ;
@@ -176,7 +172,7 @@ import { RedisSubscriber } from "../messaging/redis-subscriber";
176
172
import { UsageService } from "../orgs/usage-service" ;
177
173
import { UserService } from "../user/user-service" ;
178
174
import { SSHKeyService } from "../user/sshkey-service" ;
179
- import { StartWorkspaceOptions , WorkspaceService , mapGrpcError } from "./workspace-service" ;
175
+ import { StartWorkspaceOptions , WorkspaceService } from "./workspace-service" ;
180
176
import { GitpodTokenService } from "../user/gitpod-token-service" ;
181
177
import { EnvVarService } from "../user/env-var-service" ;
182
178
import { ScmService } from "../projects/scm-service" ;
@@ -236,8 +232,6 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
236
232
237
233
@inject ( GitTokenScopeGuesser ) private readonly gitTokenScopeGuesser : GitTokenScopeGuesser ,
238
234
239
- @inject ( HeadlessLogService ) private readonly headlessLogService : HeadlessLogService ,
240
-
241
235
@inject ( ProjectsService ) private readonly projectsService : ProjectsService ,
242
236
@inject ( ScmService ) private readonly scmService : ScmService ,
243
237
@@ -1105,36 +1099,9 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
1105
1099
1106
1100
const user = await this . checkAndBlockUser ( "sendHeartBeat" , undefined , { instanceId } ) ;
1107
1101
1108
- try {
1109
- const wsi = await this . workspaceDb . trace ( ctx ) . findInstanceById ( instanceId ) ;
1110
- if ( ! wsi ) {
1111
- throw new ApplicationError ( ErrorCodes . NOT_FOUND , "workspace does not exist" ) ;
1112
- }
1113
-
1114
- const ws = await this . workspaceDb . trace ( ctx ) . findById ( wsi . workspaceId ) ;
1115
- if ( ! ws ) {
1116
- throw new ApplicationError ( ErrorCodes . NOT_FOUND , "workspace does not exist" ) ;
1117
- }
1118
- await this . guardAccess ( { kind : "workspaceInstance" , subject : wsi , workspace : ws } , "update" ) ;
1119
-
1120
- const wasClosed = ! ! ( options && options . wasClosed ) ;
1121
- await this . workspaceDb . trace ( ctx ) . updateLastHeartbeat ( instanceId , user . id , new Date ( ) , wasClosed ) ;
1122
-
1123
- const req = new MarkActiveRequest ( ) ;
1124
- req . setId ( instanceId ) ;
1125
- req . setClosed ( wasClosed ) ;
1126
-
1127
- const client = await this . workspaceManagerClientProvider . get ( wsi . region ) ;
1128
- await client . markActive ( ctx , req ) ;
1129
- } catch ( e ) {
1130
- if ( e . message && typeof e . message === "string" && ( e . message as String ) . endsWith ( "does not exist" ) ) {
1131
- // This is an old tab with open workspace: drop silently
1132
- return ;
1133
- } else {
1134
- e = mapGrpcError ( e ) ;
1135
- throw e ;
1136
- }
1137
- }
1102
+ await this . workspaceService . sendHeartBeat ( user . id , options , ( instance , workspace ) =>
1103
+ this . guardAccess ( { kind : "workspaceInstance" , subject : instance , workspace } , "update" ) ,
1104
+ ) ;
1138
1105
}
1139
1106
1140
1107
async getWorkspaceOwner ( ctx : TraceContext , workspaceId : string ) : Promise < UserInfo | undefined > {
@@ -1872,6 +1839,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
1872
1839
return ;
1873
1840
}
1874
1841
1842
+ // TODO(gpl) Remove entirely after FGA rollout
1875
1843
const logCtx : LogContext = { userId : user . id , workspaceId } ;
1876
1844
// eslint-disable-next-line prefer-const
1877
1845
let { instance, workspace } = await this . internGetCurrentWorkspaceInstance ( ctx , user , workspaceId ) ;
@@ -1883,103 +1851,18 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
1883
1851
const teamMembers = await this . organizationService . listMembers ( user . id , workspace . organizationId ) ;
1884
1852
await this . guardAccess ( { kind : "workspaceLog" , subject : workspace , teamMembers } , "get" ) ;
1885
1853
1886
- // wait for up to 20s for imageBuildLogInfo to appear due to:
1887
- // - db-sync round-trip times
1888
- // - but also: wait until the image build actually started (image pull!), and log info is available!
1889
- for ( let i = 0 ; i < 10 ; i ++ ) {
1890
- if ( instance . imageBuildInfo ?. log ) {
1891
- break ;
1892
- }
1893
- await new Promise ( ( resolve ) => setTimeout ( resolve , 2000 ) ) ;
1894
-
1895
- const wsi = await this . workspaceDb . trace ( ctx ) . findInstanceById ( instance . id ) ;
1896
- if ( ! wsi || ! [ "preparing" , "building" ] . includes ( wsi . status . phase ) ) {
1897
- log . debug ( logCtx , `imagebuild logs: instance is not/no longer in 'building' state` , {
1898
- phase : wsi ?. status . phase ,
1899
- } ) ;
1900
- return ;
1901
- }
1902
- instance = wsi as WorkspaceInstance ; // help the compiler a bit
1903
- }
1904
-
1905
- const logInfo = instance . imageBuildInfo ?. log ;
1906
- if ( ! logInfo ) {
1907
- log . error ( logCtx , "cannot watch imagebuild logs for workspaceId: no image build info available" ) ;
1908
- throw new ApplicationError (
1909
- ErrorCodes . HEADLESS_LOG_NOT_YET_AVAILABLE ,
1910
- "cannot watch imagebuild logs for workspaceId" ,
1911
- ) ;
1912
- }
1913
-
1914
- const aborted = new Deferred < boolean > ( ) ;
1915
- try {
1916
- const logEndpoint : HeadlessLogEndpoint = {
1917
- url : logInfo . url ,
1918
- headers : logInfo . headers ,
1919
- } ;
1920
- let lineCount = 0 ;
1921
- await this . headlessLogService . streamImageBuildLog (
1922
- logCtx ,
1923
- logEndpoint ,
1924
- async ( chunk ) => {
1925
- if ( aborted . isResolved ) {
1926
- return ;
1927
- }
1928
-
1929
- try {
1930
- chunk = chunk . replace ( "\n" , WorkspaceImageBuild . LogLine . DELIMITER ) ;
1931
- lineCount += chunk . split ( WorkspaceImageBuild . LogLine . DELIMITER_REGEX ) . length ;
1932
-
1933
- client . onWorkspaceImageBuildLogs ( undefined as any , {
1934
- text : chunk ,
1935
- isDiff : true ,
1936
- upToLine : lineCount ,
1937
- } ) ;
1938
- } catch ( err ) {
1939
- log . error ( "error while streaming imagebuild logs" , err ) ;
1940
- aborted . resolve ( true ) ;
1941
- }
1942
- } ,
1943
- aborted ,
1944
- ) ;
1945
- } catch ( err ) {
1946
- // This error is most likely a temporary one (too early). We defer to the client whether they want to keep on trying or not.
1947
- log . debug ( logCtx , "cannot watch imagebuild logs for workspaceId" , err ) ;
1948
- throw new ApplicationError (
1949
- ErrorCodes . HEADLESS_LOG_NOT_YET_AVAILABLE ,
1950
- "cannot watch imagebuild logs for workspaceId" ,
1951
- ) ;
1952
- } finally {
1953
- aborted . resolve ( false ) ;
1954
- }
1854
+ await this . workspaceService . watchWorkspaceImageBuildLogs ( user . id , workspaceId , client ) ;
1955
1855
}
1956
1856
1957
1857
async getHeadlessLog ( ctx : TraceContext , instanceId : string ) : Promise < HeadlessLogUrls > {
1958
1858
traceAPIParams ( ctx , { instanceId } ) ;
1959
1859
1960
1860
const user = await this . checkAndBlockUser ( "getHeadlessLog" , { instanceId } ) ;
1961
- const logCtx : LogContext = { instanceId } ;
1962
-
1963
- const ws = await this . workspaceDb . trace ( ctx ) . findByInstanceId ( instanceId ) ;
1964
- if ( ! ws ) {
1965
- throw new ApplicationError ( ErrorCodes . NOT_FOUND , `Workspace ${ instanceId } not found` ) ;
1966
- }
1967
-
1968
- const wsiPromise = this . workspaceDb . trace ( ctx ) . findInstanceById ( instanceId ) ;
1969
- const teamMembers = await this . organizationService . listMembers ( user . id , ws . organizationId ) ;
1970
-
1971
- await this . guardAccess ( { kind : "workspaceLog" , subject : ws , teamMembers } , "get" ) ;
1972
1861
1973
- const wsi = await wsiPromise ;
1974
- if ( ! wsi ) {
1975
- throw new ApplicationError ( ErrorCodes . NOT_FOUND , `Workspace instance for ${ instanceId } not found` ) ;
1976
- }
1977
-
1978
- const urls = await this . headlessLogService . getHeadlessLogURLs ( logCtx , wsi , ws . ownerId ) ;
1979
- if ( ! urls || ( typeof urls . streams === "object" && Object . keys ( urls . streams ) . length === 0 ) ) {
1980
- throw new ApplicationError ( ErrorCodes . NOT_FOUND , `Headless logs for ${ instanceId } not found` ) ;
1981
- }
1982
- return urls ;
1862
+ return this . workspaceService . getHeadlessLog ( user . id , instanceId , async ( workspace ) => {
1863
+ const teamMembers = await this . organizationService . listMembers ( user . id , workspace . organizationId ) ;
1864
+ await this . guardAccess ( { kind : "workspaceLog" , subject : workspace , teamMembers } , "get" ) ;
1865
+ } ) ;
1983
1866
}
1984
1867
1985
1868
private async internGetCurrentWorkspaceInstance (
0 commit comments