Skip to content

Commit 35e97c1

Browse files
committed
[server] Also rate-limit 'PrebuildManager.startPrebuild' (used by apps & webhooks)
1 parent fcc70e3 commit 35e97c1

File tree

3 files changed

+25
-14
lines changed

3 files changed

+25
-14
lines changed

components/server/ee/src/prebuilds/prebuild-manager.ts

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

77
import { DBWithTracing, TracedWorkspaceDB, WorkspaceDB } from '@gitpod/gitpod-db/lib';
8-
import { CommitContext, Project, ProjectEnvVar, StartPrebuildContext, StartPrebuildResult, TaskConfig, User, WorkspaceConfig, WorkspaceInstance } from '@gitpod/gitpod-protocol';
8+
import { CommitContext, Project, ProjectEnvVar, RateLimiterError, StartPrebuildContext, StartPrebuildResult, TaskConfig, User, WorkspaceConfig, WorkspaceInstance } from '@gitpod/gitpod-protocol';
99
import { log } from '@gitpod/gitpod-protocol/lib/util/logging';
1010
import { TraceContext } from '@gitpod/gitpod-protocol/lib/util/tracing';
1111
import { inject, injectable } from 'inversify';
@@ -16,6 +16,9 @@ import { ConfigProvider } from '../../../src/workspace/config-provider';
1616
import { WorkspaceStarter } from '../../../src/workspace/workspace-starter';
1717
import { Config } from '../../../src/config';
1818
import { ProjectsService } from '../../../src/projects/projects-service';
19+
import { startPrebuild, UserRateLimiter } from '../../../src/auth/rate-limiter';
20+
import { ErrorCodes } from '@gitpod/gitpod-protocol/lib/messaging/error';
21+
import { ResponseError } from 'vscode-ws-jsonrpc';
1922

2023
export class WorkspaceRunningError extends Error {
2124
constructor(msg: string, public instance: WorkspaceInstance) {
@@ -119,12 +122,18 @@ export class PrebuildManager {
119122
const workspace = await this.workspaceFactory.createForContext({span}, user, prebuildContext, contextURL);
120123
const prebuildPromise = this.workspaceDB.trace({span}).findPrebuildByWorkspaceID(workspace.id)!;
121124

122-
// const canBuildNow = await this.prebuildRateLimiter.canBuildNow({ span }, user, cloneURL);
123-
// if (!canBuildNow) {
124-
// // we cannot start building now because the rate limiter prevents it.
125-
// span.setTag("starting", false);
126-
// return { wsid: workspace.id, done: false };;
127-
// }
125+
// rate limiting
126+
try {
127+
await UserRateLimiter.instance(this.config.rateLimiter).consume(user.id, startPrebuild);
128+
} catch (error) {
129+
span.setTag("starting", false);
130+
if (error instanceof Error) {
131+
log.error({ userId: user.id }, "Unexpected error in the rate limiter", error);
132+
throw error;
133+
}
134+
log.warn({ userId: user.id }, "Rate limiter prevents accessing method due to too many requests.", error, { method: "startPrebuild" });
135+
throw new ResponseError<RateLimiterError>(ErrorCodes.TOO_MANY_REQUESTS, "too many requests", { method: "startPrebuild", retryAfter: Math.ceil(error.msBeforeNext / 1000) || 1 });
136+
}
128137

129138
span.setTag("starting", true);
130139
const projectEnvVars = await projectEnvVarsPromise;

components/server/src/auth/rate-limiter.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import { RateLimiterMemory, RateLimiterRes } from "rate-limiter-flexible";
1111

1212
export const accessCodeSyncStorage = 'accessCodeSyncStorage';
1313
export const accessHeadlessLogs = 'accessHeadlessLogs';
14-
type GitpodServerMethodType = keyof Omit<GitpodServer, "dispose" | "setClient"> | typeof accessCodeSyncStorage | typeof accessHeadlessLogs;
15-
type GroupKey = "default" | "startWorkspace";
14+
export const startPrebuild = 'startPrebuild';
15+
type GitpodServerMethodType = keyof Omit<GitpodServer, "dispose" | "setClient"> | typeof accessCodeSyncStorage | typeof accessHeadlessLogs | typeof startPrebuild;
16+
type GroupKey = "default" | "startWorkspace" | "startPrebuild";
1617
type GroupsConfig = {
1718
[key: string]: {
1819
points: number,
@@ -161,10 +162,6 @@ function getConfig(config: RateLimiterConfig): RateLimiterConfig {
161162
"getLicenseInfo": { group: "default", points: 1 },
162163
"licenseIncludesFeature": { group: "default", points: 1 },
163164

164-
"accessCodeSyncStorage": { group: "default", points: 1 },
165-
166-
accessHeadlessLogs: { group: "default", points: 1 },
167-
168165
"adminAddStudentEmailDomain": { group: "default", points: 1 },
169166
"adminGetAccountStatement": { group: "default", points: 1 },
170167
"adminIsStudent": { group: "default", points: 1 },
@@ -197,6 +194,11 @@ function getConfig(config: RateLimiterConfig): RateLimiterConfig {
197194
"trackLocation": { group: "default", points: 1},
198195
"identifyUser": { group: "default", points: 1},
199196
"getIDEOptions": { group: "default", points: 1 },
197+
198+
// Non-server methods
199+
"accessCodeSyncStorage": { group: "default", points: 1 },
200+
"accessHeadlessLogs": { group: "default", points: 1 },
201+
"startPrebuild": { group: "startPrebuild", points: 1 },
200202
};
201203

202204
return {

components/server/src/websocket/websocket-connection-manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ class GitpodJsonRpcProxyFactory<T extends object> extends JsonRpcProxyFactory<T>
358358
throw rlRejected;
359359
}
360360
log.warn({ userId }, "Rate limiter prevents accessing method due to too many requests.", rlRejected, { method });
361-
throw new ResponseError<RateLimiterError>(ErrorCodes.TOO_MANY_REQUESTS, "too many requests", { method, retryAfter: Math.round(rlRejected.msBeforeNext / 1000) || 1 });
361+
throw new ResponseError<RateLimiterError>(ErrorCodes.TOO_MANY_REQUESTS, "too many requests", { method, retryAfter: Math.ceil(rlRejected.msBeforeNext / 1000) || 1 });
362362
}
363363

364364
// access guard

0 commit comments

Comments
 (0)