@@ -8,18 +8,22 @@ import { inject, injectable } from "inversify";
8
8
import * as grpc from "@grpc/grpc-js" ;
9
9
import { RedisPublisher , WorkspaceDB } from "@gitpod/gitpod-db/lib" ;
10
10
import {
11
+ GetWorkspaceTimeoutResult ,
11
12
GitpodServer ,
12
13
PortProtocol ,
13
14
PortVisibility ,
14
15
Project ,
16
+ SetWorkspaceTimeoutResult ,
15
17
StartWorkspaceResult ,
16
18
User ,
19
+ WORKSPACE_TIMEOUT_DEFAULT_SHORT ,
17
20
Workspace ,
18
21
WorkspaceContext ,
19
22
WorkspaceInstance ,
20
23
WorkspaceInstancePort ,
21
24
WorkspaceInstanceRepoStatus ,
22
25
WorkspaceSoftDeletion ,
26
+ WorkspaceTimeoutDuration ,
23
27
} from "@gitpod/gitpod-protocol" ;
24
28
import { ErrorCodes , ApplicationError } from "@gitpod/gitpod-protocol/lib/messaging/error" ;
25
29
import { Authorizer } from "../authorization/authorizer" ;
@@ -32,6 +36,7 @@ import {
32
36
PortProtocol as ProtoPortProtocol ,
33
37
PortSpec ,
34
38
ControlPortRequest ,
39
+ SetTimeoutRequest ,
35
40
} from "@gitpod/ws-manager/lib" ;
36
41
import { WorkspaceStarter } from "./workspace-starter" ;
37
42
import { log } from "@gitpod/gitpod-protocol/lib/util/logging" ;
@@ -45,6 +50,7 @@ import { EnvVarService } from "../user/env-var-service";
45
50
import { WorkspaceManagerClientProvider } from "@gitpod/ws-manager/lib/client-provider" ;
46
51
import { SupportedWorkspaceClass } from "@gitpod/gitpod-protocol/lib/workspace-class" ;
47
52
import { Config } from "../config" ;
53
+ import { goDurationToHumanReadable } from "@gitpod/gitpod-protocol/lib/util/timeutil" ;
48
54
49
55
export interface StartWorkspaceOptions extends GitpodServer . StartWorkspaceOptions {
50
56
/**
@@ -578,6 +584,78 @@ export class WorkspaceService {
578
584
} ) ) ;
579
585
return classes ;
580
586
}
587
+
588
+ /**
589
+ *
590
+ * @param userId
591
+ * @param workspaceId
592
+ * @param check TODO(gpl) Remove after FGA rollout
593
+ * @returns
594
+ */
595
+ public async getWorkspaceTimeout (
596
+ userId : string ,
597
+ workspaceId : string ,
598
+ check : ( instance : WorkspaceInstance , workspace : Workspace ) => Promise < void > = async ( ) => { } ,
599
+ ) : Promise < GetWorkspaceTimeoutResult > {
600
+ await this . auth . checkPermissionOnWorkspace ( userId , "access" , workspaceId ) ;
601
+
602
+ const workspace = await this . getWorkspace ( userId , workspaceId ) ;
603
+ const canChange = await this . entitlementService . maySetTimeout ( userId , workspace . organizationId ) ;
604
+
605
+ const instance = await this . db . findCurrentInstance ( workspaceId ) ;
606
+ if ( ! instance || instance . status . phase !== "running" ) {
607
+ log . warn ( { userId, workspaceId } , "Can only get keep-alive for running workspaces" ) ;
608
+ const duration = WORKSPACE_TIMEOUT_DEFAULT_SHORT ;
609
+ return { duration, canChange, humanReadableDuration : goDurationToHumanReadable ( duration ) } ;
610
+ }
611
+ await check ( instance , workspace ) ;
612
+
613
+ const req = new DescribeWorkspaceRequest ( ) ;
614
+ req . setId ( instance . id ) ;
615
+ const client = await this . clientProvider . get ( instance . region ) ;
616
+ const desc = await client . describeWorkspace ( { } , req ) ;
617
+ const duration = desc . getStatus ( ) ! . getSpec ( ) ! . getTimeout ( ) ;
618
+
619
+ return { duration, canChange, humanReadableDuration : goDurationToHumanReadable ( duration ) } ;
620
+ }
621
+
622
+ public async setWorkspaceTimeout (
623
+ userId : string ,
624
+ workspaceId : string ,
625
+ duration : WorkspaceTimeoutDuration ,
626
+ check : ( instance : WorkspaceInstance , workspace : Workspace ) => Promise < void > = async ( ) => { } ,
627
+ ) : Promise < SetWorkspaceTimeoutResult > {
628
+ await this . auth . checkPermissionOnWorkspace ( userId , "access" , workspaceId ) ;
629
+
630
+ let validatedDuration ;
631
+ try {
632
+ validatedDuration = WorkspaceTimeoutDuration . validate ( duration ) ;
633
+ } catch ( err ) {
634
+ throw new ApplicationError ( ErrorCodes . INVALID_VALUE , "Invalid duration : " + err . message ) ;
635
+ }
636
+
637
+ const workspace = await this . getWorkspace ( userId , workspaceId ) ;
638
+ if ( ! ( await this . entitlementService . maySetTimeout ( userId , workspace . organizationId ) ) ) {
639
+ throw new ApplicationError ( ErrorCodes . PLAN_PROFESSIONAL_REQUIRED , "Plan upgrade is required" ) ;
640
+ }
641
+
642
+ const instance = await this . getCurrentInstance ( userId , workspaceId ) ;
643
+ if ( instance . status . phase !== "running" || workspace . type !== "regular" ) {
644
+ throw new ApplicationError ( ErrorCodes . NOT_FOUND , "Can only set keep-alive for regular, running workspaces" ) ;
645
+ }
646
+ await check ( instance , workspace ) ;
647
+
648
+ const client = await this . clientProvider . get ( instance . region ) ;
649
+ const req = new SetTimeoutRequest ( ) ;
650
+ req . setId ( instance . id ) ;
651
+ req . setDuration ( validatedDuration ) ;
652
+ await client . setTimeout ( { } , req ) ;
653
+
654
+ return {
655
+ resetTimeoutOnWorkspaces : [ workspace . id ] ,
656
+ humanReadableDuration : goDurationToHumanReadable ( validatedDuration ) ,
657
+ } ;
658
+ }
581
659
}
582
660
583
661
// TODO(gpl) Make private after FGA rollout
0 commit comments