Skip to content

Commit 5166afd

Browse files
corneliusludmannroboquat
authored andcommitted
[installer] Allow to set default workspace timeout
1 parent 3ecb0f7 commit 5166afd

File tree

12 files changed

+113
-18
lines changed

12 files changed

+113
-18
lines changed

components/gitpod-protocol/src/gitpod-service.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,16 @@ export interface ClientHeaderFields {
321321
clientRegion?: string;
322322
}
323323

324-
export const WorkspaceTimeoutValues = ["30m", "60m", "180m"] as const;
324+
export const WORKSPACE_TIMEOUT_DEFAULT_SHORT = "short";
325+
export const WORKSPACE_TIMEOUT_DEFAULT_LONG = "long";
326+
export const WORKSPACE_TIMEOUT_EXTENDED = "extended";
327+
export const WORKSPACE_TIMEOUT_EXTENDED_ALT = "180m"; // for backwards compatibility since the IDE uses this
328+
export const WorkspaceTimeoutValues = [
329+
WORKSPACE_TIMEOUT_DEFAULT_SHORT,
330+
WORKSPACE_TIMEOUT_DEFAULT_LONG,
331+
WORKSPACE_TIMEOUT_EXTENDED,
332+
WORKSPACE_TIMEOUT_EXTENDED_ALT,
333+
] as const;
325334

326335
export const createServiceMock = function <C extends GitpodClient, S extends GitpodServer>(
327336
methods: Partial<JsonRpcProxy<S>>,

components/server/ee/src/user/eligibility-service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import { inject, injectable } from "inversify";
88
import { TeamSubscriptionDB, UserDB } from "@gitpod/gitpod-db/lib";
99
import { TokenProvider } from "../../../src/user/token-provider";
10-
import { User, WorkspaceTimeoutDuration, WorkspaceInstance } from "@gitpod/gitpod-protocol";
10+
import { User, WorkspaceTimeoutDuration, WorkspaceInstance, WORKSPACE_TIMEOUT_DEFAULT_LONG, WORKSPACE_TIMEOUT_DEFAULT_SHORT } from "@gitpod/gitpod-protocol";
1111
import { RemainingHours } from "@gitpod/gitpod-protocol/lib/accounting-protocol";
1212
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
1313
import { Plans, MAX_PARALLEL_WORKSPACES } from "@gitpod/gitpod-protocol/lib/plans";
@@ -251,9 +251,9 @@ export class EligibilityService {
251251
*/
252252
async getDefaultWorkspaceTimeout(user: User, date: Date = new Date()): Promise<WorkspaceTimeoutDuration> {
253253
if (await this.maySetTimeout(user, date)) {
254-
return "60m";
254+
return WORKSPACE_TIMEOUT_DEFAULT_LONG;
255255
} else {
256-
return "30m";
256+
return WORKSPACE_TIMEOUT_DEFAULT_SHORT;
257257
}
258258
}
259259

components/server/ee/src/user/user-service.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import { UserService, CheckSignUpParams, CheckTermsParams } from "../../../src/user/user-service";
8-
import { User, WorkspaceTimeoutDuration } from "@gitpod/gitpod-protocol";
8+
import { User, WorkspaceTimeoutDuration, WORKSPACE_TIMEOUT_EXTENDED, WORKSPACE_TIMEOUT_EXTENDED_ALT, WORKSPACE_TIMEOUT_DEFAULT_LONG, WORKSPACE_TIMEOUT_DEFAULT_SHORT } from "@gitpod/gitpod-protocol";
99
import { inject } from "inversify";
1010
import { LicenseEvaluator } from "@gitpod/licensor/lib";
1111
import { Feature } from "@gitpod/licensor/lib/api";
@@ -14,13 +14,15 @@ import { EligibilityService } from "./eligibility-service";
1414
import { SubscriptionService } from "@gitpod/gitpod-payment-endpoint/lib/accounting";
1515
import { OssAllowListDB } from "@gitpod/gitpod-db/lib/oss-allowlist-db";
1616
import { HostContextProvider } from "../../../src/auth/host-context-provider";
17+
import { Config } from "../../../src/config";
1718

1819
export class UserServiceEE extends UserService {
1920
@inject(LicenseEvaluator) protected readonly licenseEvaluator: LicenseEvaluator;
2021
@inject(EligibilityService) protected readonly eligibilityService: EligibilityService;
2122
@inject(SubscriptionService) protected readonly subscriptionService: SubscriptionService;
2223
@inject(OssAllowListDB) protected readonly OssAllowListDb: OssAllowListDB;
2324
@inject(HostContextProvider) protected readonly hostContextProvider: HostContextProvider;
25+
@inject(Config) protected readonly config: Config;
2426

2527
async getDefaultWorkspaceTimeout(user: User, date: Date): Promise<WorkspaceTimeoutDuration> {
2628
if (this.config.enablePayment) {
@@ -32,10 +34,35 @@ export class UserServiceEE extends UserService {
3234

3335
// the self-hosted case
3436
if (!this.licenseEvaluator.isEnabled(Feature.FeatureSetTimeout, userCount)) {
35-
return "30m";
37+
return WORKSPACE_TIMEOUT_DEFAULT_SHORT;
3638
}
3739

38-
return "60m";
40+
return WORKSPACE_TIMEOUT_DEFAULT_LONG;
41+
}
42+
43+
public workspaceTimeoutToDuration(timeout: WorkspaceTimeoutDuration): string {
44+
switch (timeout) {
45+
case WORKSPACE_TIMEOUT_DEFAULT_SHORT:
46+
return "30m";
47+
case WORKSPACE_TIMEOUT_DEFAULT_LONG:
48+
return this.config.workspaceDefaults.timeoutDefault || "60m";
49+
case WORKSPACE_TIMEOUT_EXTENDED:
50+
case WORKSPACE_TIMEOUT_EXTENDED_ALT:
51+
return this.config.workspaceDefaults.timeoutExtended || "180m";
52+
}
53+
}
54+
55+
public durationToWorkspaceTimeout(duration: string): WorkspaceTimeoutDuration {
56+
switch (duration) {
57+
case "30m":
58+
return WORKSPACE_TIMEOUT_DEFAULT_SHORT;
59+
case this.config.workspaceDefaults.timeoutDefault || "60m":
60+
return WORKSPACE_TIMEOUT_DEFAULT_LONG;
61+
case this.config.workspaceDefaults.timeoutExtended || "180m":
62+
return WORKSPACE_TIMEOUT_EXTENDED_ALT;
63+
default:
64+
return WORKSPACE_TIMEOUT_DEFAULT_SHORT;
65+
}
3966
}
4067

4168
async userGetsMoreResources(user: User): Promise<boolean> {

components/server/ee/src/workspace/gitpod-server-impl.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
Workspace,
4646
FindPrebuildsParams,
4747
TeamMemberRole,
48+
WORKSPACE_TIMEOUT_DEFAULT_SHORT,
4849
} from "@gitpod/gitpod-protocol";
4950
import { ResponseError } from "vscode-jsonrpc";
5051
import {
@@ -77,7 +78,7 @@ import {
7778
import { Plans } from "@gitpod/gitpod-protocol/lib/plans";
7879
import * as pThrottle from "p-throttle";
7980
import { formatDate } from "@gitpod/gitpod-protocol/lib/util/date-time";
80-
import { FindUserByIdentityStrResult } from "../../../src/user/user-service";
81+
import { FindUserByIdentityStrResult, UserService } from "../../../src/user/user-service";
8182
import {
8283
Accounting,
8384
AccountService,
@@ -133,6 +134,8 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
133134

134135
@inject(UserCounter) protected readonly userCounter: UserCounter;
135136

137+
@inject(UserService) protected readonly userService: UserService;
138+
136139
initialize(
137140
client: GitpodClient | undefined,
138141
user: User | undefined,
@@ -312,15 +315,15 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
312315
instancesWithReset.map((i) => {
313316
const req = new SetTimeoutRequest();
314317
req.setId(i.id);
315-
req.setDuration(defaultTimeout);
318+
req.setDuration(this.userService.workspaceTimeoutToDuration(defaultTimeout));
316319

317320
return client.setTimeout(ctx, req);
318321
}),
319322
);
320323

321324
const req = new SetTimeoutRequest();
322325
req.setId(runningInstance.id);
323-
req.setDuration(duration);
326+
req.setDuration(this.userService.workspaceTimeoutToDuration(duration));
324327
await client.setTimeout(ctx, req);
325328

326329
return {
@@ -343,7 +346,7 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
343346
const runningInstance = await this.workspaceDb.trace(ctx).findRunningInstance(workspaceId);
344347
if (!runningInstance) {
345348
log.warn({ userId: user.id, workspaceId }, "Can only get keep-alive for running workspaces");
346-
return { duration: "30m", canChange };
349+
return { duration: WORKSPACE_TIMEOUT_DEFAULT_SHORT, canChange };
347350
}
348351
await this.guardAccess({ kind: "workspaceInstance", subject: runningInstance, workspace: workspace }, "get");
349352

@@ -352,7 +355,7 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
352355

353356
const client = await this.workspaceManagerClientProvider.get(runningInstance.region);
354357
const desc = await client.describeWorkspace(ctx, req);
355-
const duration = desc.getStatus()!.getSpec()!.getTimeout() as WorkspaceTimeoutDuration;
358+
const duration = this.userService.durationToWorkspaceTimeout(desc.getStatus()!.getSpec()!.getTimeout());
356359
return { duration, canChange };
357360
}
358361

components/server/src/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export interface WorkspaceDefaults {
2929
workspaceImage: string;
3030
previewFeatureFlags: NamedWorkspaceFeatureFlag[];
3131
defaultFeatureFlags: NamedWorkspaceFeatureFlag[];
32+
timeoutDefault?: string;
33+
timeoutExtended?: string;
3234
}
3335

3436
export interface WorkspaceGarbageCollection {

components/server/src/user/user-service.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import { injectable, inject } from "inversify";
8-
import { User, Identity, WorkspaceTimeoutDuration, UserEnvVarValue, Token } from "@gitpod/gitpod-protocol";
8+
import { User, Identity, WorkspaceTimeoutDuration, UserEnvVarValue, Token, WORKSPACE_TIMEOUT_DEFAULT_SHORT, WORKSPACE_TIMEOUT_DEFAULT_LONG, WORKSPACE_TIMEOUT_EXTENDED, WORKSPACE_TIMEOUT_EXTENDED_ALT } from "@gitpod/gitpod-protocol";
99
import { TermsAcceptanceDB, UserDB } from "@gitpod/gitpod-db/lib";
1010
import { HostContextProvider } from "../auth/host-context-provider";
1111
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
@@ -155,7 +155,32 @@ export class UserService {
155155
* @param date The date for which we want to know the default workspace timeout
156156
*/
157157
async getDefaultWorkspaceTimeout(user: User, date: Date = new Date()): Promise<WorkspaceTimeoutDuration> {
158-
return "30m";
158+
return WORKSPACE_TIMEOUT_DEFAULT_SHORT;
159+
}
160+
161+
public workspaceTimeoutToDuration(timeout: WorkspaceTimeoutDuration): string {
162+
switch (timeout) {
163+
case WORKSPACE_TIMEOUT_DEFAULT_SHORT:
164+
return "30m";
165+
case WORKSPACE_TIMEOUT_DEFAULT_LONG:
166+
return "60m";
167+
case WORKSPACE_TIMEOUT_EXTENDED:
168+
case WORKSPACE_TIMEOUT_EXTENDED_ALT:
169+
return "180m";
170+
}
171+
}
172+
173+
public durationToWorkspaceTimeout(duration: string): WorkspaceTimeoutDuration {
174+
switch (duration) {
175+
case "30m":
176+
return WORKSPACE_TIMEOUT_DEFAULT_SHORT;
177+
case "60m":
178+
return WORKSPACE_TIMEOUT_DEFAULT_LONG;
179+
case "180m":
180+
return WORKSPACE_TIMEOUT_EXTENDED_ALT;
181+
default:
182+
return WORKSPACE_TIMEOUT_DEFAULT_SHORT;
183+
}
159184
}
160185

161186
/**

components/server/src/workspace/workspace-starter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1191,7 +1191,7 @@ export class WorkspaceStarter {
11911191
spec.setWorkspaceLocation(workspace.config.workspaceLocation || spec.getCheckoutLocation());
11921192
spec.setFeatureFlagsList(this.toWorkspaceFeatureFlags(featureFlags));
11931193
if (workspace.type === "regular") {
1194-
spec.setTimeout(await userTimeoutPromise);
1194+
spec.setTimeout(this.userService.workspaceTimeoutToDuration(await userTimeoutPromise));
11951195
}
11961196
spec.setAdmission(admissionLevel);
11971197
return spec;

install/installer/example-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ openVSX:
2323
url: https://open-vsx.org
2424
repository: eu.gcr.io/gitpod-core-dev/build
2525
workspace:
26+
maxLifetime: 36h0m0s
2627
resources:
2728
requests:
2829
cpu: "1"

install/installer/pkg/components/server/configmap.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
4040
WorkspaceImage: common.ImageName(common.ThirdPartyContainerRepo(ctx.Config.Repository, ""), workspace.DefaultWorkspaceImage, workspace.DefaultWorkspaceImageVersion),
4141
PreviewFeatureFlags: []NamedWorkspaceFeatureFlag{},
4242
DefaultFeatureFlags: []NamedWorkspaceFeatureFlag{},
43+
TimeoutDefault: ctx.Config.Workspace.TimeoutDefault,
44+
TimeoutExtended: ctx.Config.Workspace.TimeoutExtended,
4345
},
4446
Session: Session{
4547
MaxAgeMs: 259200000,

install/installer/pkg/components/server/types.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
package server
66

7-
import "github.com/gitpod-io/gitpod/installer/pkg/config/v1"
7+
import (
8+
"github.com/gitpod-io/gitpod/common-go/util"
9+
"github.com/gitpod-io/gitpod/installer/pkg/config/v1"
10+
)
811

912
// These types are from TypeScript files
1013

@@ -120,6 +123,8 @@ type WorkspaceDefaults struct {
120123
WorkspaceImage string `json:"workspaceImage"`
121124
PreviewFeatureFlags []NamedWorkspaceFeatureFlag `json:"previewFeatureFlags"`
122125
DefaultFeatureFlags []NamedWorkspaceFeatureFlag `json:"defaultFeatureFlags"`
126+
TimeoutDefault *util.Duration `json:"timeoutDefault,omitempty"`
127+
TimeoutExtended *util.Duration `json:"timeoutExtended,omitempty"`
123128
}
124129

125130
type NamedWorkspaceFeatureFlag string

install/installer/pkg/components/ws-manager/configmap.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
3838
return (&q).String()
3939
}
4040

41+
timeoutAfterClose := util.Duration(2 * time.Minute)
42+
if ctx.Config.Workspace.TimeoutAfterClose != nil {
43+
timeoutAfterClose = *ctx.Config.Workspace.TimeoutAfterClose
44+
}
45+
4146
wsmcfg := config.ServiceConfiguration{
4247
Manager: config.Configuration{
4348
Namespace: ctx.Namespace,
@@ -81,11 +86,11 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
8186
WorkspaceHostPath: wsdaemon.HostWorkingArea,
8287
WorkspacePodTemplate: templatesCfg,
8388
Timeouts: config.WorkspaceTimeoutConfiguration{
84-
AfterClose: util.Duration(2 * time.Minute),
89+
AfterClose: timeoutAfterClose,
8590
HeadlessWorkspace: util.Duration(1 * time.Hour),
8691
Initialization: util.Duration(30 * time.Minute),
8792
RegularWorkspace: util.Duration(30 * time.Minute),
88-
MaxLifetime: util.Duration(36 * time.Hour),
93+
MaxLifetime: ctx.Config.Workspace.MaxLifetime,
8994
TotalStartup: util.Duration(1 * time.Hour),
9095
ContentFinalization: util.Duration(1 * time.Hour),
9196
Stopping: util.Duration(1 * time.Hour),

install/installer/pkg/config/v1/config.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
package config
66

77
import (
8+
"time"
9+
10+
"github.com/gitpod-io/gitpod/common-go/util"
811
"github.com/gitpod-io/gitpod/installer/pkg/config"
912
"github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental"
1013
"github.com/gitpod-io/gitpod/ws-daemon/pkg/cpulimit"
@@ -53,6 +56,7 @@ func (v version) Defaults(in interface{}) error {
5356
cfg.Workspace.Runtime.FSShiftMethod = FSShiftFuseFS
5457
cfg.Workspace.Runtime.ContainerDSocket = "/run/containerd/containerd.sock"
5558
cfg.Workspace.Runtime.ContainerDRuntimeDir = "/var/lib/containerd/io.containerd.runtime.v2.task/k8s.io"
59+
cfg.Workspace.MaxLifetime = util.Duration(36 * time.Hour)
5660
cfg.OpenVSX.URL = "https://open-vsx.org"
5761
cfg.DisableDefinitelyGP = false
5862

@@ -224,6 +228,18 @@ type Workspace struct {
224228
Runtime WorkspaceRuntime `json:"runtime" validate:"required"`
225229
Resources Resources `json:"resources" validate:"required"`
226230
Templates *WorkspaceTemplates `json:"templates,omitempty"`
231+
232+
// MaxLifetime is the maximum time a workspace is allowed to run. After that, the workspace times out despite activity
233+
MaxLifetime util.Duration `json:"maxLifetime" validate:"required"`
234+
235+
// TimeoutDefault is the default timeout of a regular workspace
236+
TimeoutDefault *util.Duration `json:"timeoutDefault,omitempty"`
237+
238+
// TimeoutExtended is the workspace timeout that a user can extend to for one workspace
239+
TimeoutExtended *util.Duration `json:"timeoutExtended,omitempty"`
240+
241+
// TimeoutAfterClose is the time a workspace timed out after it has been closed (“closed” means that it does not get a heartbeat from an IDE anymore)
242+
TimeoutAfterClose *util.Duration `json:"timeoutAfterClose,omitempty"`
227243
}
228244

229245
type OpenVSX struct {

0 commit comments

Comments
 (0)