Skip to content

Commit 553f9b2

Browse files
authored
[server] Migrate all ports-related API calls to WorkspaceService (#18527)
1 parent b95a468 commit 553f9b2

File tree

3 files changed

+207
-118
lines changed

3 files changed

+207
-118
lines changed

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

Lines changed: 6 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import {
2828
GitpodToken,
2929
GitpodTokenType,
3030
PermissionName,
31-
PortVisibility,
3231
PrebuiltWorkspace,
3332
PrebuiltWorkspaceContext,
3433
SetWorkspaceTimeoutResult,
@@ -69,7 +68,6 @@ import {
6968
PrebuildEvent,
7069
RoleOrPermission,
7170
WORKSPACE_TIMEOUT_DEFAULT_SHORT,
72-
PortProtocol,
7371
WorkspaceInstanceRepoStatus,
7472
} from "@gitpod/gitpod-protocol";
7573
import { BlockedRepository } from "@gitpod/gitpod-protocol/lib/blocked-repositories-protocol";
@@ -101,13 +99,9 @@ import { WorkspaceManagerClientProvider } from "@gitpod/ws-manager/lib/client-pr
10199
import {
102100
AdmissionLevel,
103101
ControlAdmissionRequest,
104-
ControlPortRequest,
105102
DescribeWorkspaceRequest,
106103
MarkActiveRequest,
107-
PortSpec,
108-
PortVisibility as ProtoPortVisibility,
109104
SetTimeoutRequest,
110-
PortProtocol as ProtoPortProtocol,
111105
StopWorkspacePolicy,
112106
TakeSnapshotRequest,
113107
} from "@gitpod/ws-manager/lib/core_pb";
@@ -158,7 +152,6 @@ import { EntitlementService } from "../billing/entitlement-service";
158152
import { formatPhoneNumber } from "../user/phone-numbers";
159153
import { IDEService } from "../ide-service";
160154
import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution";
161-
import * as grpc from "@grpc/grpc-js";
162155
import { CostCenterJSON } from "@gitpod/gitpod-protocol/lib/usage";
163156
import { createCookielessId, maskIp } from "../analytics";
164157
import {
@@ -188,7 +181,7 @@ import { RedisSubscriber } from "../messaging/redis-subscriber";
188181
import { UsageService } from "../orgs/usage-service";
189182
import { UserService } from "../user/user-service";
190183
import { SSHKeyService } from "../user/sshkey-service";
191-
import { StartWorkspaceOptions, WorkspaceService } from "./workspace-service";
184+
import { StartWorkspaceOptions, WorkspaceService, mapGrpcError } from "./workspace-service";
192185
import { GitpodTokenService } from "../user/gitpod-token-service";
193186
import { EnvVarService } from "../user/env-var-service";
194187

@@ -1143,7 +1136,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
11431136
// This is an old tab with open workspace: drop silently
11441137
return;
11451138
} else {
1146-
e = this.mapGrpcError(e);
1139+
e = mapGrpcError(e);
11471140
throw e;
11481141
}
11491142
}
@@ -1837,7 +1830,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
18371830
traceAPIParams(ctx, { workspaceId });
18381831
traceWI(ctx, { workspaceId });
18391832

1840-
await this.checkAndBlockUser("getOpenPorts");
1833+
const user = await this.checkAndBlockUser("getOpenPorts");
18411834

18421835
const instance = await this.workspaceDb.trace(ctx).findRunningInstance(workspaceId);
18431836
const workspace = await this.workspaceDb.trace(ctx).findById(workspaceId);
@@ -1847,30 +1840,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
18471840

18481841
await this.guardAccess({ kind: "workspaceInstance", subject: instance, workspace }, "get");
18491842

1850-
const req = new DescribeWorkspaceRequest();
1851-
req.setId(instance.id);
1852-
const client = await this.workspaceManagerClientProvider.get(instance.region);
1853-
const desc = await client.describeWorkspace(ctx, req);
1854-
1855-
if (!desc.hasStatus()) {
1856-
throw new Error("describeWorkspace returned no status");
1857-
}
1858-
1859-
const status = desc.getStatus()!;
1860-
const ports = status
1861-
.getSpec()!
1862-
.getExposedPortsList()
1863-
.map(
1864-
(p) =>
1865-
<WorkspaceInstancePort>{
1866-
port: p.getPort(),
1867-
url: p.getUrl(),
1868-
visibility: this.portVisibilityFromProto(p.getVisibility()),
1869-
protocol: this.portProtocolFromProto(p.getProtocol()),
1870-
},
1871-
);
1872-
1873-
return ports;
1843+
return await this.workspaceService.getOpenPorts(user.id, workspaceId);
18741844
}
18751845

18761846
public async updateGitStatus(
@@ -1924,61 +1894,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
19241894
traceWI(ctx, { instanceId: runningInstance.id });
19251895
await this.guardAccess({ kind: "workspaceInstance", subject: runningInstance, workspace }, "update");
19261896

1927-
const req = new ControlPortRequest();
1928-
req.setId(runningInstance.id);
1929-
const spec = new PortSpec();
1930-
spec.setPort(port.port);
1931-
spec.setVisibility(this.portVisibilityToProto(port.visibility));
1932-
spec.setProtocol(this.portProtocolToProto(port.protocol));
1933-
req.setSpec(spec);
1934-
req.setExpose(true);
1935-
1936-
try {
1937-
const client = await this.workspaceManagerClientProvider.get(runningInstance.region);
1938-
await client.controlPort(ctx, req);
1939-
} catch (e) {
1940-
throw this.mapGrpcError(e);
1941-
}
1942-
}
1943-
1944-
private portVisibilityFromProto(visibility: ProtoPortVisibility): PortVisibility {
1945-
switch (visibility) {
1946-
default: // the default in the protobuf def is: private
1947-
case ProtoPortVisibility.PORT_VISIBILITY_PRIVATE:
1948-
return "private";
1949-
case ProtoPortVisibility.PORT_VISIBILITY_PUBLIC:
1950-
return "public";
1951-
}
1952-
}
1953-
1954-
private portVisibilityToProto(visibility: PortVisibility | undefined): ProtoPortVisibility {
1955-
switch (visibility) {
1956-
default: // the default for requests is: private
1957-
case "private":
1958-
return ProtoPortVisibility.PORT_VISIBILITY_PRIVATE;
1959-
case "public":
1960-
return ProtoPortVisibility.PORT_VISIBILITY_PUBLIC;
1961-
}
1962-
}
1963-
1964-
private portProtocolFromProto(protocol: ProtoPortProtocol): PortProtocol {
1965-
switch (protocol) {
1966-
default: // the default in the protobuf def is: http
1967-
case ProtoPortProtocol.PORT_PROTOCOL_HTTP:
1968-
return "http";
1969-
case ProtoPortProtocol.PORT_PROTOCOL_HTTPS:
1970-
return "https";
1971-
}
1972-
}
1973-
1974-
private portProtocolToProto(protocol: PortProtocol | undefined): ProtoPortProtocol {
1975-
switch (protocol) {
1976-
default: // the default for requests is: http
1977-
case "http":
1978-
return ProtoPortProtocol.PORT_PROTOCOL_HTTP;
1979-
case "https":
1980-
return ProtoPortProtocol.PORT_PROTOCOL_HTTPS;
1981-
}
1897+
return await this.workspaceService.openPort(user.id, workspaceId, port);
19821898
}
19831899

19841900
public async closePort(ctx: TraceContext, workspaceId: string, port: number) {
@@ -1999,15 +1915,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
19991915
traceWI(ctx, { instanceId: instance.id });
20001916
await this.guardAccess({ kind: "workspaceInstance", subject: instance, workspace }, "update");
20011917

2002-
const req = new ControlPortRequest();
2003-
req.setId(instance.id);
2004-
const spec = new PortSpec();
2005-
spec.setPort(port);
2006-
req.setSpec(spec);
2007-
req.setExpose(false);
2008-
2009-
const client = await this.workspaceManagerClientProvider.get(instance.region);
2010-
await client.controlPort(ctx, req);
1918+
await this.workspaceService.closePort(user.id, workspaceId, port);
20111919
}
20121920

20131921
async watchWorkspaceImageBuildLogs(ctx: TraceContext, workspaceId: string): Promise<void> {
@@ -3523,23 +3431,6 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
35233431
increaseDashboardErrorBoundaryCounter();
35243432
}
35253433

3526-
private mapGrpcError(err: Error): Error {
3527-
function isGrpcError(err: any): err is grpc.StatusObject {
3528-
return err.code && err.details;
3529-
}
3530-
3531-
if (!isGrpcError(err)) {
3532-
return err;
3533-
}
3534-
3535-
switch (err.code) {
3536-
case grpc.status.RESOURCE_EXHAUSTED:
3537-
return new ApplicationError(ErrorCodes.TOO_MANY_REQUESTS, err.details);
3538-
default:
3539-
return new ApplicationError(ErrorCodes.INTERNAL_SERVER_ERROR, err.details);
3540-
}
3541-
}
3542-
35433434
async getIDToken(): Promise<void> {}
35443435

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

components/server/src/workspace/workspace-service.spec.db.ts

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

77
import { TypeORM } from "@gitpod/gitpod-db/lib";
88
import { resetDB } from "@gitpod/gitpod-db/lib/test/reset-db";
9-
import { CommitContext, Organization, Project, User, WorkspaceConfig } from "@gitpod/gitpod-protocol";
9+
import {
10+
CommitContext,
11+
Organization,
12+
Project,
13+
User,
14+
WorkspaceConfig,
15+
WorkspaceInstancePort,
16+
} from "@gitpod/gitpod-protocol";
1017
import { Experiments } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server";
1118
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
1219
import * as chai from "chai";
@@ -259,6 +266,42 @@ describe("WorkspaceService", async () => {
259266

260267
await expectError(ErrorCodes.NOT_FOUND, svc.setDescription(stranger.id, ws.id, desc));
261268
});
269+
270+
it("should getOpenPorts", async () => {
271+
const svc = container.get(WorkspaceService);
272+
const ws = await createTestWorkspace(svc, org, owner, project);
273+
274+
await expectError(
275+
ErrorCodes.NOT_FOUND,
276+
svc.getOpenPorts(owner.id, ws.id),
277+
"should fail on non-running workspace",
278+
);
279+
});
280+
281+
it("should openPort", async () => {
282+
const svc = container.get(WorkspaceService);
283+
const ws = await createTestWorkspace(svc, org, owner, project);
284+
285+
const port: WorkspaceInstancePort = {
286+
port: 8080,
287+
};
288+
await expectError(
289+
ErrorCodes.NOT_FOUND,
290+
svc.openPort(owner.id, ws.id, port),
291+
"should fail on non-running workspace",
292+
);
293+
});
294+
295+
it("should closePort", async () => {
296+
const svc = container.get(WorkspaceService);
297+
const ws = await createTestWorkspace(svc, org, owner, project);
298+
299+
await expectError(
300+
ErrorCodes.NOT_FOUND,
301+
svc.closePort(owner.id, ws.id, 8080),
302+
"should fail on non-running workspace",
303+
);
304+
});
262305
});
263306

264307
async function createTestWorkspace(svc: WorkspaceService, org: Organization, owner: User, project: Project) {

0 commit comments

Comments
 (0)