77import { injectable , inject } from "inversify" ;
88import { GitpodServerImpl } from "../../../src/workspace/gitpod-server-impl" ;
99import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing" ;
10- import { GitpodServer , GitpodClient , AdminGetListRequest , User , AdminGetListResult , Permission , AdminBlockUserRequest , AdminModifyRoleOrPermissionRequest , RoleOrPermission , AdminModifyPermanentWorkspaceFeatureFlagRequest , UserFeatureSettings , AdminGetWorkspacesRequest , WorkspaceAndInstance , GetWorkspaceTimeoutResult , WorkspaceTimeoutDuration , WorkspaceTimeoutValues , SetWorkspaceTimeoutResult , WorkspaceContext , CreateWorkspaceMode , WorkspaceCreationResult , PrebuiltWorkspaceContext , CommitContext , PrebuiltWorkspace , PermissionName , WorkspaceInstance , EduEmailDomain , ProviderRepository , Queue , PrebuildWithStatus , CreateProjectParams , Project , StartPrebuildResult , ClientHeaderFields } from "@gitpod/gitpod-protocol" ;
10+ import { GitpodServer , GitpodClient , AdminGetListRequest , User , AdminGetListResult , Permission , AdminBlockUserRequest , AdminModifyRoleOrPermissionRequest , RoleOrPermission , AdminModifyPermanentWorkspaceFeatureFlagRequest , UserFeatureSettings , AdminGetWorkspacesRequest , WorkspaceAndInstance , GetWorkspaceTimeoutResult , WorkspaceTimeoutDuration , WorkspaceTimeoutValues , SetWorkspaceTimeoutResult , WorkspaceContext , CreateWorkspaceMode , WorkspaceCreationResult , PrebuiltWorkspaceContext , CommitContext , PrebuiltWorkspace , PermissionName , WorkspaceInstance , EduEmailDomain , ProviderRepository , Queue , PrebuildWithStatus , CreateProjectParams , Project , StartPrebuildResult , ClientHeaderFields , Workspace } from "@gitpod/gitpod-protocol" ;
1111import { ResponseError } from "vscode-jsonrpc" ;
1212import { TakeSnapshotRequest , AdmissionLevel , ControlAdmissionRequest , StopWorkspacePolicy , DescribeWorkspaceRequest , SetTimeoutRequest } from "@gitpod/ws-manager/lib" ;
1313import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error" ;
@@ -41,6 +41,7 @@ import { Chargebee as chargebee } from '@gitpod/gitpod-payment-endpoint/lib/char
4141import { GitHubAppSupport } from "../github/github-app-support" ;
4242import { GitLabAppSupport } from "../gitlab/gitlab-app-support" ;
4343import { Config } from "../../../src/config" ;
44+ import { SnapshotService } from "./snapshot-service" ;
4445
4546@injectable ( )
4647export class GitpodServerEEImpl extends GitpodServerImpl < GitpodClient , GitpodServer > {
@@ -73,6 +74,8 @@ export class GitpodServerEEImpl extends GitpodServerImpl<GitpodClient, GitpodSer
7374
7475 @inject ( Config ) protected readonly config : Config ;
7576
77+ @inject ( SnapshotService ) protected readonly snapshotService : SnapshotService ;
78+
7679 initialize ( client : GitpodClient | undefined , user : User , accessGuard : ResourceAccessGuard , clientHeaderFields : ClientHeaderFields ) : void {
7780 super . initialize ( client , user , accessGuard , clientHeaderFields ) ;
7881 this . listenToCreditAlerts ( ) ;
@@ -370,18 +373,13 @@ export class GitpodServerEEImpl extends GitpodServerImpl<GitpodClient, GitpodSer
370373 span . setTag ( "userId" , user . id ) ;
371374
372375 try {
373- const workspace = await this . workspaceDb . trace ( { span } ) . findById ( workspaceId ) ;
374- if ( ! workspace || workspace . ownerId !== user . id ) {
375- throw new ResponseError ( ErrorCodes . NOT_FOUND , `Workspace ${ workspaceId } does not exist.` ) ;
376- }
376+ const workspace = await this . guardSnaphotAccess ( span , user . id , workspaceId ) ;
377377
378378 const instance = await this . workspaceDb . trace ( { span } ) . findRunningInstance ( workspaceId ) ;
379379 if ( ! instance ) {
380380 throw new ResponseError ( ErrorCodes . NOT_FOUND , `Workspace ${ workspaceId } has no running instance` ) ;
381381 }
382-
383382 await this . guardAccess ( { kind : "workspaceInstance" , subject : instance , workspace} , "get" ) ;
384- await this . guardAccess ( { kind : "snapshot" , subject : undefined , workspaceOwnerID : workspace . ownerId , workspaceID : workspace . id } , "create" ) ;
385383
386384 const client = await this . workspaceManagerClientProvider . get ( instance . region ) ;
387385 const request = new TakeSnapshotRequest ( ) ;
@@ -392,18 +390,63 @@ export class GitpodServerEEImpl extends GitpodServerImpl<GitpodClient, GitpodSer
392390 const resp = await client . takeSnapshot ( { span } , request ) ;
393391
394392 const id = uuidv4 ( )
395- this . workspaceDb . trace ( { span } ) . storeSnapshot ( {
393+ await this . workspaceDb . trace ( { span } ) . storeSnapshot ( {
396394 id,
397395 creationTime : new Date ( ) . toISOString ( ) ,
396+ state : 'available' ,
398397 bucketId : resp . getUrl ( ) ,
399398 originalWorkspaceId : workspaceId ,
400- layoutData
399+ layoutData,
401400 } ) ;
402401
403402 return id ;
404- } catch ( e ) {
405- TraceContext . logError ( { span } , e ) ;
406- throw e ;
403+ } catch ( err ) {
404+ TraceContext . logError ( { span } , err ) ;
405+ throw err ;
406+ } finally {
407+ span . finish ( )
408+ }
409+ }
410+
411+ protected async guardSnaphotAccess ( span : opentracing . Span , userId : string , workspaceId : string ) : Promise < Workspace > {
412+ const workspace = await this . workspaceDb . trace ( { span } ) . findById ( workspaceId ) ;
413+ if ( ! workspace || workspace . ownerId !== userId ) {
414+ throw new ResponseError ( ErrorCodes . NOT_FOUND , `Workspace ${ workspaceId } does not exist.` ) ;
415+ }
416+ await this . guardAccess ( { kind : "snapshot" , subject : undefined , workspaceOwnerID : workspace . ownerId , workspaceID : workspace . id } , "create" ) ;
417+
418+ return workspace ;
419+ }
420+
421+ /**
422+ * @param snapshotId
423+ * @throws ResponseError with either NOT_FOUND or SNAPSHOT_ERROR in case the snapshot is not done yet.
424+ */
425+ async waitForSnapshot ( snapshotId : string ) : Promise < void > {
426+ this . requireEELicense ( Feature . FeatureSnapshot ) ;
427+
428+ const user = this . checkAndBlockUser ( "waitForSnapshot" ) ;
429+
430+ const span = opentracing . globalTracer ( ) . startSpan ( "waitForSnapshot" ) ;
431+ span . setTag ( "snapshotId" , snapshotId ) ;
432+ span . setTag ( "userId" , user . id ) ;
433+
434+ try {
435+ const snapshot = await this . workspaceDb . trace ( { span } ) . findSnapshotById ( snapshotId ) ;
436+ if ( ! snapshot ) {
437+ throw new ResponseError ( ErrorCodes . NOT_FOUND , `No snapshot with id '${ snapshotId } ' found.` )
438+ }
439+ const snapshotWorkspace = await this . guardSnaphotAccess ( span , user . id , snapshot . originalWorkspaceId ) ;
440+
441+ try {
442+ await this . snapshotService . driveSnapshot ( snapshotWorkspace . ownerId , snapshot ) ;
443+ } catch ( err ) {
444+ // wrap in SNAPSHOT_ERROR to signal this call should not be retried.
445+ throw new ResponseError ( ErrorCodes . SNAPSHOT_ERROR , err . toString ( ) ) ;
446+ }
447+ } catch ( err ) {
448+ TraceContext . logError ( { span } , err ) ;
449+ throw err ;
407450 } finally {
408451 span . finish ( )
409452 }
0 commit comments