Skip to content

Commit b43c266

Browse files
geroplakosyakov
authored andcommitted
[server] WorkspaceService.startWorkspace - part I: API-side, no tests
1 parent 73533e4 commit b43c266

File tree

2 files changed

+181
-130
lines changed

2 files changed

+181
-130
lines changed

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

Lines changed: 16 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ import { Deferred } from "@gitpod/gitpod-protocol/lib/util/deferred";
157157
import { ListUsageRequest, ListUsageResponse } from "@gitpod/gitpod-protocol/lib/usage";
158158
import { VerificationService } from "../auth/verification-service";
159159
import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode";
160-
import { EntitlementService, MayStartWorkspaceResult } from "../billing/entitlement-service";
160+
import { EntitlementService } from "../billing/entitlement-service";
161161
import { formatPhoneNumber } from "../user/phone-numbers";
162162
import { IDEService } from "../ide-service";
163163
import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution";
@@ -169,8 +169,6 @@ import {
169169
getExperimentsClientForBackend,
170170
} from "@gitpod/gitpod-protocol/lib/experiments/configcat-server";
171171
import { increaseDashboardErrorBoundaryCounter, reportCentralizedPermsValidation } from "../prometheus-metrics";
172-
import { RegionService } from "./region-service";
173-
import { isWorkspaceRegion, WorkspaceRegion } from "@gitpod/gitpod-protocol/lib/workspace-cluster";
174172
import { EnvVarService } from "./env-var-service";
175173
import { LinkedInService } from "../linkedin-service";
176174
import { SnapshotService, WaitForSnapshotOptions } from "./snapshot-service";
@@ -920,15 +918,11 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
920918

921919
const user = await this.checkAndBlockUser("startWorkspace", undefined, { workspaceId });
922920

921+
// (gpl) We keep this check here for backwards compatibility, it should be superfluous in the future
923922
const workspace = await this.workspaceService.getWorkspace(user.id, workspaceId);
924-
const mayStartPromise = this.mayStartWorkspace(
925-
ctx,
926-
user,
927-
workspace.organizationId,
928-
this.workspaceDb.trace(ctx).findRegularRunningInstances(user.id),
929-
);
930923
await this.guardAccess({ kind: "workspace", subject: workspace }, "get");
931924

925+
// (gpl) We keep this check here for backwards compatibility, it should be superfluous in the future
932926
const runningInstance = await this.workspaceDb.trace(ctx).findRunningInstance(workspace.id);
933927
if (runningInstance) {
934928
traceWI(ctx, { instanceId: runningInstance.id });
@@ -944,38 +938,15 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
944938
};
945939
}
946940

947-
if (!!workspace.softDeleted) {
948-
throw new ApplicationError(ErrorCodes.NOT_FOUND, "Workspace not found!");
949-
}
950-
941+
// (gpl) We keep this check here for backwards compatibility, it should be superfluous in the future
951942
// no matter if the workspace is shared or not, you cannot create a new instance
952943
await this.guardAccess({ kind: "workspaceInstance", subject: undefined, workspace }, "create");
953944

954-
if (workspace.type !== "regular") {
955-
throw new ApplicationError(ErrorCodes.PERMISSION_DENIED, "Cannot (re-)start irregular workspace.");
956-
}
957-
958-
if (workspace.deleted) {
959-
throw new ApplicationError(ErrorCodes.PERMISSION_DENIED, "Cannot (re-)start a deleted workspace.");
960-
}
961-
const envVarsPromise = this.envVarService.resolve(workspace);
962-
const projectPromise = workspace.projectId
963-
? ApplicationError.notFoundToUndefined(this.projectsService.getProject(user.id, workspace.projectId))
964-
: Promise.resolve(undefined);
965-
966-
await mayStartPromise;
967-
968-
options.region = await this.determineWorkspaceRegion(workspace, options.region || "");
969-
970-
// at this point we're about to actually start a new workspace
971-
const result = await this.workspaceStarter.startWorkspace(
972-
ctx,
973-
workspace,
974-
user,
975-
await projectPromise,
976-
await envVarsPromise,
977-
options,
978-
);
945+
const opts = {
946+
...options,
947+
clientCountryCode: this.clientHeaderFields.clientRegion,
948+
};
949+
const result = await this.workspaceService.startWorkspace(user, workspaceId, opts);
979950
traceWI(ctx, { instanceId: result.instanceID });
980951
return result;
981952
}
@@ -1405,7 +1376,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
14051376
? (await this.projectsService.findProjectsByCloneUrl(user.id, context.repository.cloneUrl))[0]
14061377
: undefined;
14071378

1408-
const mayStartWorkspacePromise = this.mayStartWorkspace(
1379+
const mayStartWorkspacePromise = this.workspaceService.mayStartWorkspace(
14091380
ctx,
14101381
user,
14111382
options.organizationId,
@@ -1448,19 +1419,14 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
14481419
throw err;
14491420
}
14501421

1451-
const envVarsPromise = this.envVarService.resolve(workspace);
1452-
options.region = await this.determineWorkspaceRegion(workspace, options.region || "");
1453-
14541422
logContext.workspaceId = workspace.id;
14551423
traceWI(ctx, { workspaceId: workspace.id });
1456-
const startWorkspaceResult = await this.workspaceStarter.startWorkspace(
1457-
ctx,
1458-
workspace,
1459-
user,
1460-
project,
1461-
await envVarsPromise,
1462-
options,
1463-
);
1424+
1425+
const opts = {
1426+
...options,
1427+
clientCountryCode: this.clientHeaderFields.clientRegion,
1428+
};
1429+
const startWorkspaceResult = await this.workspaceService.startWorkspace(user, workspace.id, opts);
14641430
ctx.span?.log({ event: "startWorkspaceComplete", ...startWorkspaceResult });
14651431

14661432
return {
@@ -1664,41 +1630,6 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
16641630
return result;
16651631
}
16661632

1667-
private async mayStartWorkspace(
1668-
ctx: TraceContext,
1669-
user: User,
1670-
organizationId: string,
1671-
runningInstances: Promise<WorkspaceInstance[]>,
1672-
): Promise<void> {
1673-
let result: MayStartWorkspaceResult = {};
1674-
try {
1675-
result = await this.entitlementService.mayStartWorkspace(user, organizationId, runningInstances);
1676-
TraceContext.addNestedTags(ctx, { mayStartWorkspace: { result } });
1677-
} catch (err) {
1678-
log.error({ userId: user.id }, "EntitlementSerivce.mayStartWorkspace error", err);
1679-
TraceContext.setError(ctx, err);
1680-
return; // we don't want to block workspace starts because of internal errors
1681-
}
1682-
if (!!result.needsVerification) {
1683-
throw new ApplicationError(ErrorCodes.NEEDS_VERIFICATION, `Please verify your account.`);
1684-
}
1685-
if (!!result.usageLimitReachedOnCostCenter) {
1686-
throw new ApplicationError(
1687-
ErrorCodes.PAYMENT_SPENDING_LIMIT_REACHED,
1688-
"Increase usage limit and try again.",
1689-
{
1690-
attributionId: result.usageLimitReachedOnCostCenter,
1691-
},
1692-
);
1693-
}
1694-
if (!!result.hitParallelWorkspaceLimit) {
1695-
throw new ApplicationError(
1696-
ErrorCodes.TOO_MANY_RUNNING_WORKSPACES,
1697-
`You cannot run more than ${result.hitParallelWorkspaceLimit.max} workspaces at the same time. Please stop a workspace before starting another one.`,
1698-
);
1699-
}
1700-
}
1701-
17021633
public async getFeaturedRepositories(ctx: TraceContext): Promise<WhitelistedRepository[]> {
17031634
const user = await this.checkAndBlockUser("getFeaturedRepositories");
17041635
const repositories = await this.workspaceDb.trace(ctx).getFeaturedRepositories();
@@ -3782,50 +3713,6 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
37823713
}
37833714
}
37843715

3785-
private async determineWorkspaceRegion(ws: Workspace, preference: WorkspaceRegion): Promise<WorkspaceRegion> {
3786-
const guessWorkspaceRegionEnabled = await getExperimentsClientForBackend().getValueAsync(
3787-
"guessWorkspaceRegion",
3788-
false,
3789-
{
3790-
user: { id: this.userID || "" },
3791-
},
3792-
);
3793-
3794-
const regionLogContext = {
3795-
requested_region: preference,
3796-
client_region_from_header: this.clientHeaderFields.clientRegion,
3797-
experiment_enabled: false,
3798-
guessed_region: "",
3799-
};
3800-
3801-
let targetRegion = preference;
3802-
if (!isWorkspaceRegion(preference)) {
3803-
targetRegion = "";
3804-
} else {
3805-
targetRegion = preference;
3806-
}
3807-
3808-
if (guessWorkspaceRegionEnabled) {
3809-
regionLogContext.experiment_enabled = true;
3810-
3811-
if (!preference) {
3812-
// Attempt to identify the region based on LoadBalancer headers, if there was no explicit choice on the request.
3813-
// The Client region contains the two letter country code.
3814-
if (this.clientHeaderFields.clientRegion) {
3815-
const countryCode = this.clientHeaderFields.clientRegion;
3816-
3817-
targetRegion = RegionService.countryCodeToNearestWorkspaceRegion(countryCode);
3818-
regionLogContext.guessed_region = targetRegion;
3819-
}
3820-
}
3821-
}
3822-
3823-
const logCtx = { userId: this.userID, workspaceId: ws.id };
3824-
log.info(logCtx, "[guessWorkspaceRegion] Workspace with region selection", regionLogContext);
3825-
3826-
return targetRegion;
3827-
}
3828-
38293716
async getIDToken(): Promise<void> {}
38303717

38313718
public async controlAdmission(ctx: TraceContext, workspaceId: string, level: "owner" | "everyone"): Promise<void> {

0 commit comments

Comments
 (0)