Skip to content

Commit 6be4928

Browse files
geroplroboquat
authored andcommitted
[server] Share ClientMetadata and trace clientType
1 parent 9972d25 commit 6be4928

File tree

5 files changed

+45
-36
lines changed

5 files changed

+45
-36
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import { GitLabAppSupport } from "../gitlab/gitlab-app-support";
4040
import { Config } from "../../../src/config";
4141
import { SnapshotService, WaitForSnapshotOptions } from "./snapshot-service";
4242
import { SafePromise } from "@gitpod/gitpod-protocol/lib/util/safe-promise";
43+
import { ClientMetadata } from "../../../src/websocket/websocket-connection-manager";
4344

4445
@injectable()
4546
export class GitpodServerEEImpl extends GitpodServerImpl {
@@ -72,8 +73,8 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
7273

7374
@inject(SnapshotService) protected readonly snapshotService: SnapshotService;
7475

75-
initialize(client: GitpodClient | undefined, user: User | undefined, accessGuard: ResourceAccessGuard, clientHeaderFields: ClientHeaderFields): void {
76-
super.initialize(client, user, accessGuard, clientHeaderFields);
76+
initialize(client: GitpodClient | undefined, user: User | undefined, accessGuard: ResourceAccessGuard, clientMetadata: ClientMetadata, clientHeaderFields: ClientHeaderFields): void {
77+
super.initialize(client, user, accessGuard, clientMetadata, clientHeaderFields);
7778

7879
this.listenToCreditAlerts();
7980
this.listenForPrebuildUpdates();

components/server/src/server.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { RabbitMQConsensusLeaderMessenger } from './consensus/rabbitmq-consensus
2929
import { WorkspaceGarbageCollector } from './workspace/garbage-collector';
3030
import { WorkspaceDownloadService } from './workspace/workspace-download-service';
3131
import { MonitoringEndpointsApp } from './monitoring-endpoints';
32-
import { WebsocketClientType, WebsocketConnectionManager } from './websocket/websocket-connection-manager';
32+
import { WebsocketConnectionManager } from './websocket/websocket-connection-manager';
3333
import { DeletedEntryGC, PeriodicDbDeleter, TypeORM } from '@gitpod/gitpod-db/lib';
3434
import { OneTimeSecretServer } from './one-time-secret-server';
3535
import { Disposable, DisposableCollection, GitpodClient, GitpodServer } from '@gitpod/gitpod-protocol';
@@ -164,7 +164,7 @@ export class Server<C extends GitpodClient, S extends GitpodServer> {
164164
if (info.req.url === '/v1') {
165165
try {
166166
await this.bearerAuth.auth(info.req as express.Request)
167-
} catch (e) {
167+
} catch (e) {
168168
if (isBearerAuthError(e)) {
169169
return callback(false, 401, e.message);
170170
}
@@ -319,8 +319,8 @@ export class Server<C extends GitpodClient, S extends GitpodServer> {
319319
help: 'Currently served websocket connections',
320320
labelNames: ["clientType"],
321321
});
322-
this.websocketConnectionHandler.onConnectionCreated((_, req) => gauge.inc({ clientType: WebsocketClientType.getClientType(req) || "undefined" }));
323-
this.websocketConnectionHandler.onConnectionClosed((_, req) => gauge.dec({ clientType: WebsocketClientType.getClientType(req) || "undefined" }));
322+
this.websocketConnectionHandler.onConnectionCreated((s, _) => gauge.inc({ clientType: s.clientMetadata.type || "undefined" }));
323+
this.websocketConnectionHandler.onConnectionClosed((s, _) => gauge.dec({ clientType: s.clientMetadata.type || "undefined" }));
324324
}
325325

326326
protected installWebsocketClientContextGauge() {
@@ -329,7 +329,7 @@ export class Server<C extends GitpodClient, S extends GitpodServer> {
329329
help: 'Currently served client contexts',
330330
labelNames: ["authLevel"],
331331
});
332-
this.websocketConnectionHandler.onClientContextCreated((ctx) => gauge.inc({ authLevel: ctx.authLevel }));
333-
this.websocketConnectionHandler.onClientContextClosed((ctx) => gauge.dec({ authLevel: ctx.authLevel }));
332+
this.websocketConnectionHandler.onClientContextCreated((ctx) => gauge.inc({ authLevel: ctx.clientMetadata.authLevel }));
333+
this.websocketConnectionHandler.onClientContextClosed((ctx) => gauge.dec({ authLevel: ctx.clientMetadata.authLevel }));
334334
}
335335
}

components/server/src/user/enforcement-endpoint.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ResponseError } from 'vscode-jsonrpc';
1717
import { ErrorCodes } from '@gitpod/gitpod-protocol/lib/messaging/error';
1818
import { GitpodServerImpl } from '../workspace/gitpod-server-impl';
1919
import { ResourceAccessGuard, OwnerResourceGuard } from '../auth/resource-access';
20+
import { ClientMetadata } from '../websocket/websocket-connection-manager';
2021

2122
export const EnforcementControllerServerFactory = Symbol('EnforcementControllerServerFactory');
2223
export type EnforcementControllerServerFactory = () => GitpodServerImpl;
@@ -47,7 +48,7 @@ export class EnforcementController {
4748
another architecture is not necessary.
4849
*/
4950
const server = this.serverFactory()
50-
server.initialize(undefined, user, resourceAccessGuard, {});
51+
server.initialize(undefined, user, resourceAccessGuard, ClientMetadata.from(user.id), {});
5152
return server;
5253
}
5354

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

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const EVENT_CLIENT_CONTEXT_CLOSED = "EVENT_CLIENT_CONTEXT_CLOSED";
3131

3232
/** TODO(gpl) Refine this list */
3333
export type WebsocketClientType = "browser" | "go-client" | "vs-code";
34-
export namespace WebsocketClientType {
34+
namespace WebsocketClientType {
3535
export function getClientType(req: express.Request): WebsocketClientType | undefined {
3636
const userAgent = req.headers["user-agent"];
3737

@@ -58,9 +58,10 @@ export interface ClientMetadata {
5858
authLevel: WebsocketAuthenticationLevel,
5959
sessionId?: string;
6060
userId?: string;
61+
type?: WebsocketClientType;
6162
}
6263
export namespace ClientMetadata {
63-
export function from(userId: string | undefined, sessionId?: string): ClientMetadata {
64+
export function from(userId: string | undefined, sessionId?: string, type?: WebsocketClientType): ClientMetadata {
6465
let id = "anonymous";
6566
let authLevel: WebsocketAuthenticationLevel = "anonymous";
6667
if (userId) {
@@ -70,25 +71,22 @@ export namespace ClientMetadata {
7071
id = `session-${sessionId}`;
7172
authLevel = "session";
7273
}
73-
return { id, authLevel, userId, sessionId };
74+
return { id, authLevel, userId, sessionId, type };
7475
}
7576
}
7677

7778
export class WebsocketClientContext {
7879
constructor(
79-
/**
80-
* We try to be as specific as we can when identifying client connections.
81-
* If we now the userId, this will be the userId. If we just have a session, this is the sessionId (prefixed by `session-`).
82-
* If it's a
83-
*/
84-
public readonly clientId: string,
85-
86-
public readonly authLevel: WebsocketAuthenticationLevel
80+
public readonly clientMetadata: ClientMetadata,
8781
) {}
8882

8983
/** This list of endpoints serving client connections 1-1 */
9084
protected servers: GitpodServerImpl[] = [];
9185

86+
get clientId(): string {
87+
return this.clientMetadata.id;
88+
}
89+
9290
addEndpoint(server: GitpodServerImpl) {
9391
this.servers.push(server);
9492
}
@@ -124,7 +122,7 @@ export class WebsocketConnectionManager implements ConnectionHandler {
124122
this.createProxyTarget.bind(this),
125123
this.createAccessGuard.bind(this),
126124
this.createRateLimiter.bind(this),
127-
this.getClientId.bind(this),
125+
this.getClientMetadata.bind(this),
128126
);
129127
}
130128

@@ -167,7 +165,7 @@ export class WebsocketConnectionManager implements ConnectionHandler {
167165
clientRegion: takeFirst(expressReq.headers["x-glb-client-region"]),
168166
};
169167

170-
gitpodServer.initialize(client, user, resourceGuard, clientHeaderFields);
168+
gitpodServer.initialize(client, user, resourceGuard, clientContext.clientMetadata, clientHeaderFields);
171169
client.onDidCloseConnection(() => {
172170
gitpodServer.dispose();
173171
increaseApiConnectionClosedCounter();
@@ -193,25 +191,26 @@ export class WebsocketConnectionManager implements ConnectionHandler {
193191
}
194192

195193
protected getOrCreateClientContext(expressReq: express.Request): WebsocketClientContext {
196-
const { id: clientId, authLevel } = this.getClientId(expressReq);
197-
let ctx = this.contexts.get(clientId);
194+
const metadata = this.getClientMetadata(expressReq);
195+
let ctx = this.contexts.get(metadata.id);
198196
if (!ctx) {
199-
ctx = new WebsocketClientContext(clientId, authLevel);
200-
this.contexts.set(clientId, ctx);
197+
ctx = new WebsocketClientContext(metadata);
198+
this.contexts.set(metadata.id, ctx);
201199
this.events.emit(EVENT_CLIENT_CONTEXT_CREATED, ctx);
202200
}
203201
return ctx;
204202
}
205203

206-
protected getClientId(req?: object): ClientMetadata {
204+
protected getClientMetadata(req?: object): ClientMetadata {
207205
const expressReq = req as express.Request;
208206
const user = expressReq.user;
209207
const sessionId = expressReq.session?.id;
210-
return ClientMetadata.from(user?.id, sessionId);
208+
const type = WebsocketClientType.getClientType(expressReq);
209+
return ClientMetadata.from(user?.id, sessionId, type);
211210
}
212211

213212
protected createRateLimiter(req?: object): RateLimiter {
214-
const { id: clientId } = this.getClientId(req);
213+
const { id: clientId } = this.getClientMetadata(req);
215214
return {
216215
user: clientId,
217216
consume: (method) => UserRateLimiter.instance(this.rateLimiterConfig).consume(clientId, method),
@@ -294,12 +293,7 @@ class GitpodJsonRpcProxyFactory<T extends object> extends JsonRpcProxyFactory<T>
294293
const userId = this.clientMetadata.userId;
295294
try {
296295
// generic tracing data
297-
TraceContext.addNestedTags(ctx, {
298-
client: {
299-
id: this.clientMetadata.id,
300-
authLevel: this.clientMetadata.authLevel,
301-
},
302-
});
296+
traceClientMetadata(ctx, this.clientMetadata);
303297
TraceContext.setOWI(ctx, {
304298
userId,
305299
sessionId: this.clientMetadata.sessionId,
@@ -353,4 +347,14 @@ class GitpodJsonRpcProxyFactory<T extends object> extends JsonRpcProxyFactory<T>
353347
throw new ResponseError(RPCErrorCodes.InvalidRequest, "notifications are not supported");
354348
}
355349

350+
}
351+
352+
function traceClientMetadata(ctx: TraceContext, clientMetadata: ClientMetadata) {
353+
TraceContext.addNestedTags(ctx, {
354+
client: {
355+
id: clientMetadata.id,
356+
authLevel: clientMetadata.authLevel,
357+
type: clientMetadata.type,
358+
},
359+
});
356360
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import { CachingBlobServiceClientProvider } from '@gitpod/content-service/lib/su
5454
import { IDEOptions } from '@gitpod/gitpod-protocol/lib/ide-protocol';
5555
import { IDEConfigService } from '../ide-config';
5656
import { PartialProject } from '@gitpod/gitpod-protocol/src/teams-projects-protocol';
57+
import { ClientMetadata } from '../websocket/websocket-connection-manager';
5758

5859
// shortcut
5960
export const traceWI = (ctx: TraceContext, wi: Omit<LogContext, "userId">) => TraceContext.setOWI(ctx, wi); // userId is already taken care of in WebsocketConnectionManager
@@ -111,6 +112,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
111112

112113
/** Id the uniquely identifies this server instance */
113114
public readonly uuid: string = uuidv4();
115+
public readonly clientMetadata: ClientMetadata;
114116
protected clientHeaderFields: ClientHeaderFields;
115117
protected resourceAccessGuard: ResourceAccessGuard;
116118
protected client: GitpodApiClient | undefined;
@@ -123,14 +125,15 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
123125
this.disposables.dispose();
124126
}
125127

126-
initialize(client: GitpodApiClient | undefined, user: User | undefined, accessGuard: ResourceAccessGuard, clientHeaderFields: ClientHeaderFields): void {
128+
initialize(client: GitpodApiClient | undefined, user: User | undefined, accessGuard: ResourceAccessGuard, clientMetadata: ClientMetadata, clientHeaderFields: ClientHeaderFields): void {
127129
if (client) {
128130
this.disposables.push(Disposable.create(() => this.client = undefined));
129131
}
130132
this.client = client;
131133
this.user = user;
132134
this.resourceAccessGuard = accessGuard;
133135
this.clientHeaderFields = clientHeaderFields;
136+
(this.clientMetadata as any) = clientMetadata;
134137

135138
log.debug({ userId: this.user?.id }, `clientRegion: ${clientHeaderFields.clientRegion}`);
136139
log.debug({ userId: this.user?.id }, 'initializeClient');

0 commit comments

Comments
 (0)