Skip to content

Commit 0605514

Browse files
svenefftingeroboquat
authored andcommitted
[server] Allow options on ws start
introduces IDE and workspace class arguments on WS Start
1 parent f9b429b commit 0605514

File tree

5 files changed

+62
-23
lines changed

5 files changed

+62
-23
lines changed

components/gitpod-protocol/go/gitpod-service.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -1899,7 +1899,9 @@ type WorkspaceInstanceStatus struct {
18991899

19001900
// StartWorkspaceOptions is the StartWorkspaceOptions message type
19011901
type StartWorkspaceOptions struct {
1902-
ForceDefaultImage bool `json:"forceDefaultImage,omitempty"`
1902+
ForceDefaultImage bool `json:"forceDefaultImage,omitempty"`
1903+
WorkspaceClass string `json:"workspaceClass,omitempty"`
1904+
IdeSettings *IDESettings `json:"ideSettings,omitempty"`
19031905
}
19041906

19051907
// GetWorkspaceTimeoutResult is the GetWorkspaceTimeoutResult message type

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

+7-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
PrebuiltWorkspace,
2727
UserSSHPublicKeyValue,
2828
SSHPublicKeyValue,
29+
IDESettings,
2930
} from "./protocol";
3031
import {
3132
Team,
@@ -414,16 +415,20 @@ export namespace GitpodServer {
414415
export interface GetAccountStatementOptions {
415416
date?: string;
416417
}
417-
export interface CreateWorkspaceOptions {
418+
export interface CreateWorkspaceOptions extends StartWorkspaceOptions {
418419
contextUrl: string;
420+
419421
// whether running workspaces on the same context should be ignored. If false (default) users will be asked.
420422
ignoreRunningWorkspaceOnSameCommit?: boolean;
421423
ignoreRunningPrebuild?: boolean;
422424
allowUsingPreviousPrebuilds?: boolean;
423425
forceDefaultConfig?: boolean;
424426
}
427+
425428
export interface StartWorkspaceOptions {
426-
forceDefaultImage: boolean;
429+
forceDefaultImage?: boolean;
430+
workspaceClass?: string;
431+
ideSettings?: IDESettings;
427432
}
428433
export interface TakeSnapshotOptions {
429434
workspaceId: string;

components/server/src/ide-service.ts

+18-5
Original file line numberDiff line numberDiff line change
@@ -84,18 +84,28 @@ export class IDEService {
8484
return newIDESettings;
8585
}
8686

87-
async resolveWorkspaceConfig(workspace: Workspace, user: User): Promise<ResolveWorkspaceConfigResponse> {
87+
async resolveWorkspaceConfig(
88+
workspace: Workspace,
89+
user: User,
90+
userSelectedIdeSettings?: IDESettings,
91+
): Promise<ResolveWorkspaceConfigResponse> {
8892
const use = await this.configCatClientFactory().getValueAsync("use_IDEService_ResolveWorkspaceConfig", false, {
8993
user,
9094
});
9195
if (use) {
92-
return this.doResolveWorkspaceConfig(workspace, user);
96+
return this.doResolveWorkspaceConfig(
97+
workspace,
98+
userSelectedIdeSettings || user.additionalData?.ideSettings,
99+
);
93100
}
94101

95102
const deprecated = await this.resolveDeprecated(workspace, user);
96103
// assert against ide-service
97104
(async () => {
98-
const config = await this.doResolveWorkspaceConfig(workspace, user);
105+
const config = await this.doResolveWorkspaceConfig(
106+
workspace,
107+
userSelectedIdeSettings || user.additionalData?.ideSettings,
108+
);
99109
const { tasks: configTasks, ...newConfig } = config;
100110
const { tasks: deprecatedTasks, ...newDeprecated } = deprecated;
101111
// we omit tasks because we're going to rewrite them soon and the deepEqual was failing
@@ -104,14 +114,17 @@ export class IDEService {
104114
return deprecated;
105115
}
106116

107-
private async doResolveWorkspaceConfig(workspace: Workspace, user: User): Promise<ResolveWorkspaceConfigResponse> {
117+
private async doResolveWorkspaceConfig(
118+
workspace: Workspace,
119+
userSelectedIdeSettings?: IDESettings,
120+
): Promise<ResolveWorkspaceConfigResponse> {
108121
const workspaceType =
109122
workspace.type === "prebuild" ? IdeServiceApi.WorkspaceType.PREBUILD : IdeServiceApi.WorkspaceType.REGULAR;
110123

111124
const req: IdeServiceApi.ResolveWorkspaceConfigRequest = {
112125
type: workspaceType,
113126
context: JSON.stringify(workspace.context),
114-
ideSettings: JSON.stringify(user.additionalData?.ideSettings),
127+
ideSettings: JSON.stringify(userSelectedIdeSettings),
115128
workspaceConfig: JSON.stringify(workspace.config),
116129
};
117130
for (let attempt = 0; attempt < 15; attempt++) {

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

+2-3
Original file line numberDiff line numberDiff line change
@@ -734,9 +734,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
734734
await projectPromise,
735735
await userEnvVars,
736736
await projectEnvVarsPromise,
737-
{
738-
forceDefaultImage: !!options.forceDefaultImage,
739-
},
737+
options,
740738
);
741739
traceWI(ctx, { instanceId: result.instanceID });
742740
return result;
@@ -1207,6 +1205,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
12071205
project,
12081206
await envVars,
12091207
await projectEnvVarsPromise,
1208+
options,
12101209
);
12111210
ctx.span?.log({ event: "startWorkspaceComplete", ...startWorkspaceResult });
12121211

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

+32-12
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ import {
6060
EnvVarWithValue,
6161
BillingTier,
6262
Project,
63+
GitpodServer,
64+
IDESettings,
6365
} from "@gitpod/gitpod-protocol";
6466
import { IAnalyticsWriter } from "@gitpod/gitpod-protocol/lib/analytics";
6567
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
@@ -127,9 +129,8 @@ import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution";
127129
import { LogContext } from "@gitpod/gitpod-protocol/lib/util/logging";
128130
import { repeat } from "@gitpod/gitpod-protocol/lib/util/repeat";
129131

130-
export interface StartWorkspaceOptions {
132+
export interface StartWorkspaceOptions extends GitpodServer.StartWorkspaceOptions {
131133
rethrow?: boolean;
132-
forceDefaultImage?: boolean;
133134
excludeFeatureFlags?: NamedWorkspaceFeatureFlag[];
134135
}
135136

@@ -139,21 +140,31 @@ const INSTANCE_START_RETRY_INTERVAL_SECONDS = 2;
139140
export async function getWorkspaceClassForInstance(
140141
ctx: TraceContext,
141142
workspace: Workspace,
143+
previousInstance: WorkspaceInstance | undefined,
142144
user: User,
143145
project: Project | undefined,
146+
workspaceClassOverride: string | undefined,
144147
entitlementService: EntitlementService,
145148
config: WorkspaceClassesConfig,
146149
): Promise<string> {
147150
const span = TraceContext.startSpan("getWorkspaceClassForInstance", ctx);
148151
try {
149152
let workspaceClass: string | undefined;
150-
switch (workspace.type) {
151-
case "prebuild":
152-
workspaceClass = project?.settings?.workspaceClasses?.prebuild;
153-
break;
154-
case "regular":
155-
workspaceClass = project?.settings?.workspaceClasses?.regular;
156-
break;
153+
if (workspaceClassOverride) {
154+
workspaceClass = workspaceClassOverride;
155+
}
156+
if (!workspaceClass && previousInstance) {
157+
workspaceClass = previousInstance.workspaceClass;
158+
}
159+
if (!workspaceClass) {
160+
switch (workspace.type) {
161+
case "prebuild":
162+
workspaceClass = project?.settings?.workspaceClasses?.prebuild;
163+
break;
164+
case "regular":
165+
workspaceClass = project?.settings?.workspaceClasses?.regular;
166+
break;
167+
}
157168
}
158169
if (!workspaceClass && (await entitlementService.userGetsMoreResources(user))) {
159170
workspaceClass = config.find((c) => !!c.marker?.moreResources)?.id;
@@ -269,7 +280,7 @@ export class WorkspaceStarter {
269280
}
270281
}
271282

272-
const ideConfig = await this.resolveIDEConfiguration(ctx, workspace, user);
283+
const ideConfig = await this.resolveIDEConfiguration(ctx, workspace, user, options.ideSettings);
273284

274285
// create and store instance
275286
let instance = await this.workspaceDb
@@ -283,6 +294,7 @@ export class WorkspaceStarter {
283294
project,
284295
options.excludeFeatureFlags || [],
285296
ideConfig,
297+
options.workspaceClass,
286298
),
287299
);
288300
span.log({ newInstance: instance.id });
@@ -350,15 +362,20 @@ export class WorkspaceStarter {
350362
}
351363
}
352364

353-
private async resolveIDEConfiguration(ctx: TraceContext, workspace: Workspace, user: User) {
365+
private async resolveIDEConfiguration(
366+
ctx: TraceContext,
367+
workspace: Workspace,
368+
user: User,
369+
userSelectedIdeSettings?: IDESettings,
370+
) {
354371
const span = TraceContext.startSpan("resolveIDEConfiguration", ctx);
355372
try {
356373
const migrated = this.ideService.migrateSettings(user);
357374
if (user.additionalData?.ideSettings && migrated) {
358375
user.additionalData.ideSettings = migrated;
359376
}
360377

361-
const resp = await this.ideService.resolveWorkspaceConfig(workspace, user);
378+
const resp = await this.ideService.resolveWorkspaceConfig(workspace, user, userSelectedIdeSettings);
362379
if (!user.additionalData?.ideSettings && resp.refererIde) {
363380
// A user does not have IDE settings configured yet configure it with a referrer ide as default.
364381
const additionalData = user?.additionalData || {};
@@ -770,6 +787,7 @@ export class WorkspaceStarter {
770787
project: Project | undefined,
771788
excludeFeatureFlags: NamedWorkspaceFeatureFlag[],
772789
ideConfig: IdeServiceApi.ResolveWorkspaceConfigResponse,
790+
workspaceClassOverride?: string,
773791
): Promise<WorkspaceInstance> {
774792
const span = TraceContext.startSpan("newInstance", ctx);
775793
try {
@@ -814,8 +832,10 @@ export class WorkspaceStarter {
814832
let workspaceClass = await getWorkspaceClassForInstance(
815833
ctx,
816834
workspace,
835+
previousInstance,
817836
user,
818837
project,
838+
workspaceClassOverride,
819839
this.entitlementService,
820840
this.config.workspaceClasses,
821841
);

0 commit comments

Comments
 (0)