Skip to content

Commit d6dcda8

Browse files
authored
Migrate parts of gPRC workspaceService (#19129)
* Migrate parts of WorkspaceService * bump version
1 parent 56b3dd5 commit d6dcda8

File tree

18 files changed

+1869
-303
lines changed

18 files changed

+1869
-303
lines changed

components/dashboard/src/data/setup.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import * as SSHClasses from "@gitpod/public-api/lib/gitpod/v1/ssh_pb";
3131
// This is used to version the cache
3232
// If data we cache changes in a non-backwards compatible way, increment this version
3333
// That will bust any previous cache versions a client may have stored
34-
const CACHE_VERSION = "9";
34+
const CACHE_VERSION = "10";
3535

3636
export function noPersistence(queryKey: QueryKey): QueryKey {
3737
return [...queryKey, "no-persistence"];

components/dashboard/src/data/workspaces/default-workspace-image-query.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,26 @@
77
import { useQuery } from "@tanstack/react-query";
88
import { getGitpodService } from "../../service/service";
99
import { GetDefaultWorkspaceImageResult } from "@gitpod/gitpod-protocol";
10+
import { GetWorkspaceDefaultImageResponse } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
11+
import { workspaceClient } from "../../service/public-api";
1012

1113
export const useDefaultWorkspaceImageQuery = (workspaceId?: string) => {
1214
return useQuery<GetDefaultWorkspaceImageResult>({
1315
queryKey: ["default-workspace-image", { workspaceId }],
1416
staleTime: 1000 * 60 * 10, // 10 minute
1517
queryFn: async () => {
18+
// without `workspaceId` getDefaultWorkspaceImage will return org setting and if not set fallback to installation
1619
return await getGitpodService().server.getDefaultWorkspaceImage({ workspaceId });
1720
},
1821
});
1922
};
23+
24+
export const useWorkspaceDefaultImageQuery = (workspaceId: string) => {
25+
return useQuery<GetWorkspaceDefaultImageResponse>({
26+
queryKey: ["default-workspace-image-v2", { workspaceId }],
27+
staleTime: 1000 * 60 * 10, // 10 minute
28+
queryFn: async () => {
29+
return await workspaceClient.getWorkspaceDefaultImage({ workspaceId });
30+
},
31+
});
32+
};

components/dashboard/src/service/json-rpc-workspace-client.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ import {
1818
WatchWorkspaceStatusResponse,
1919
ListWorkspacesRequest,
2020
ListWorkspacesResponse,
21+
GetWorkspaceDefaultImageRequest,
22+
GetWorkspaceDefaultImageResponse,
23+
GetWorkspaceEditorCredentialsRequest,
24+
GetWorkspaceEditorCredentialsResponse,
25+
GetWorkspaceOwnerTokenRequest,
26+
GetWorkspaceOwnerTokenResponse,
27+
SendHeartBeatRequest,
28+
SendHeartBeatResponse,
29+
WorkspacePhase_Phase,
30+
GetWorkspaceDefaultImageResponse_Source,
2131
} from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
2232
import { converter } from "./public-api";
2333
import { getGitpodService } from "./service";
@@ -160,4 +170,74 @@ export class JsonRpcWorkspaceClient implements PromiseClient<typeof WorkspaceSer
160170
result.workspace = workspace.workspace;
161171
return result;
162172
}
173+
174+
async getWorkspaceDefaultImage(
175+
request: PartialMessage<GetWorkspaceDefaultImageRequest>,
176+
_options?: CallOptions | undefined,
177+
): Promise<GetWorkspaceDefaultImageResponse> {
178+
if (!request.workspaceId) {
179+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
180+
}
181+
const response = await getGitpodService().server.getDefaultWorkspaceImage({
182+
workspaceId: request.workspaceId,
183+
});
184+
const result = new GetWorkspaceDefaultImageResponse();
185+
result.defaultWorkspaceImage = response.image;
186+
switch (response.source) {
187+
case "installation":
188+
result.source = GetWorkspaceDefaultImageResponse_Source.INSTALLATION;
189+
break;
190+
case "organization":
191+
result.source = GetWorkspaceDefaultImageResponse_Source.ORGANIZATION;
192+
break;
193+
}
194+
return result;
195+
}
196+
197+
async sendHeartBeat(
198+
request: PartialMessage<SendHeartBeatRequest>,
199+
_options?: CallOptions | undefined,
200+
): Promise<SendHeartBeatResponse> {
201+
if (!request.workspaceId) {
202+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
203+
}
204+
const workspace = await this.getWorkspace({ workspaceId: request.workspaceId });
205+
if (
206+
!workspace.workspace?.status?.phase ||
207+
workspace.workspace.status.phase.name !== WorkspacePhase_Phase.RUNNING
208+
) {
209+
throw new ApplicationError(ErrorCodes.PRECONDITION_FAILED, "workspace is not running");
210+
}
211+
await getGitpodService().server.sendHeartBeat({
212+
instanceId: workspace.workspace.status.instanceId,
213+
wasClosed: request.disconnected === true,
214+
});
215+
return new SendHeartBeatResponse();
216+
}
217+
218+
async getWorkspaceOwnerToken(
219+
request: PartialMessage<GetWorkspaceOwnerTokenRequest>,
220+
_options?: CallOptions | undefined,
221+
): Promise<GetWorkspaceOwnerTokenResponse> {
222+
if (!request.workspaceId) {
223+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
224+
}
225+
const ownerToken = await getGitpodService().server.getOwnerToken(request.workspaceId);
226+
const result = new GetWorkspaceOwnerTokenResponse();
227+
result.ownerToken = ownerToken;
228+
return result;
229+
}
230+
231+
async getWorkspaceEditorCredentials(
232+
request: PartialMessage<GetWorkspaceEditorCredentialsRequest>,
233+
_options?: CallOptions | undefined,
234+
): Promise<GetWorkspaceEditorCredentialsResponse> {
235+
if (!request.workspaceId) {
236+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "workspaceId is required");
237+
}
238+
const credentials = await getGitpodService().server.getIDECredentials(request.workspaceId);
239+
const result = new GetWorkspaceEditorCredentialsResponse();
240+
result.editorCredentials = credentials;
241+
return result;
242+
}
163243
}

components/dashboard/src/service/service.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { GitpodHostUrl } from "@gitpod/gitpod-protocol/lib/util/gitpod-host-url"
1919
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
2020
import { IDEFrontendDashboardService } from "@gitpod/gitpod-protocol/lib/frontend-dashboard-service";
2121
import { RemoteTrackMessage } from "@gitpod/gitpod-protocol/lib/analytics";
22-
import { helloService } from "./public-api";
22+
import { helloService, workspaceClient } from "./public-api";
2323
import { getExperimentsClient } from "../experiments/client";
2424
import { ConnectError, Code } from "@connectrpc/connect";
2525
import { instrumentWebSocket } from "./metrics";
@@ -237,7 +237,9 @@ export class IDEFrontendService implements IDEFrontendDashboardService.IServer {
237237
const [user, listener, ideCredentials] = await Promise.all([
238238
this.service.server.getLoggedInUser(),
239239
this.service.listenToInstance(this.workspaceID),
240-
this.service.server.getIDECredentials(this.workspaceID),
240+
workspaceClient
241+
.getWorkspaceEditorCredentials({ workspaceId: this.workspaceID })
242+
.then((resp) => resp.editorCredentials),
241243
]);
242244
this.user = user;
243245
this.ideCredentials = ideCredentials;
@@ -299,8 +301,8 @@ export class IDEFrontendService implements IDEFrontendDashboardService.IServer {
299301
}
300302

301303
private activeHeartbeat(): void {
302-
if (this.instanceID) {
303-
this.service.server.sendHeartBeat({ instanceId: this.instanceID });
304+
if (this.workspaceID) {
305+
workspaceClient.sendHeartBeat({ workspaceId: this.workspaceID });
304306
}
305307
}
306308

components/dashboard/src/start/StartPage.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import { useDocumentTitle } from "../hooks/use-document-title";
1212
import gitpodIcon from "../icons/gitpod.svg";
1313
import { gitpodHostUrl } from "../service/service";
1414
import { VerifyModal } from "./VerifyModal";
15-
import { useDefaultWorkspaceImageQuery } from "../data/workspaces/default-workspace-image-query";
15+
import { useWorkspaceDefaultImageQuery } from "../data/workspaces/default-workspace-image-query";
16+
import { GetWorkspaceDefaultImageResponse_Source } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
1617

1718
export enum StartPhase {
1819
Checking = 0,
@@ -133,9 +134,14 @@ function StartError(props: { error: StartWorkspaceError }) {
133134
}
134135

135136
function WarningView(props: { workspaceId?: string; showLatestIdeWarning?: boolean; error?: StartWorkspaceError }) {
136-
const { data: imageInfo } = useDefaultWorkspaceImageQuery(props.workspaceId);
137+
const { data: imageInfo } = useWorkspaceDefaultImageQuery(props.workspaceId ?? "");
137138
let useWarning: "latestIde" | "orgImage" | undefined = props.showLatestIdeWarning ? "latestIde" : undefined;
138-
if (props.error && props.workspaceId && imageInfo?.source === "organization") {
139+
if (
140+
props.error &&
141+
props.workspaceId &&
142+
imageInfo &&
143+
imageInfo.source === GetWorkspaceDefaultImageResponse_Source.ORGANIZATION
144+
) {
139145
useWarning = "orgImage";
140146
}
141147
return (

components/dashboard/src/start/StartWorkspace.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ import Alert from "../components/Alert";
2222
import { workspaceClient, workspacesService } from "../service/public-api";
2323
import { watchWorkspaceStatus } from "../data/workspaces/listen-to-workspace-ws-messages";
2424
import { Button } from "@podkit/buttons/Button";
25-
import { GetWorkspaceRequest, StartWorkspaceRequest, StartWorkspaceResponse, Workspace, WorkspacePhase_Phase } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
25+
import {
26+
GetWorkspaceRequest,
27+
StartWorkspaceRequest,
28+
StartWorkspaceResponse,
29+
Workspace,
30+
WorkspacePhase_Phase,
31+
} from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
2632
import { PartialMessage } from "@bufbuild/protobuf";
2733

2834
const sessionId = v4();
@@ -575,10 +581,13 @@ export default class StartWorkspace extends React.Component<StartWorkspaceProps,
575581
{
576582
title: "Connect via SSH",
577583
onClick: async () => {
578-
const ownerToken = await getGitpodService().server.getOwnerToken(
579-
this.props.workspaceId,
580-
);
581-
this.setState({ isSSHModalVisible: true, ownerToken });
584+
const response = await workspaceClient.getWorkspaceOwnerToken({
585+
workspaceId: this.props.workspaceId,
586+
});
587+
this.setState({
588+
isSSHModalVisible: true,
589+
ownerToken: response.ownerToken,
590+
});
582591
},
583592
},
584593
{

components/dashboard/src/workspaces/WorkspaceOverflowMenu.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ import { ItemFieldContextMenu } from "../components/ItemsList";
1111
import { useStopWorkspaceMutation } from "../data/workspaces/stop-workspace-mutation";
1212
import { useToggleWorkspacedPinnedMutation } from "../data/workspaces/toggle-workspace-pinned-mutation";
1313
import { useToggleWorkspaceSharedMutation } from "../data/workspaces/toggle-workspace-shared-mutation";
14-
import { getGitpodService } from "../service/service";
1514
import ConnectToSSHModal from "./ConnectToSSHModal";
1615
import { DeleteWorkspaceModal } from "./DeleteWorkspaceModal";
1716
import { useToast } from "../components/toasts/Toasts";
1817
import { RenameWorkspaceModal } from "./RenameWorkspaceModal";
1918
import { AdmissionLevel, Workspace, WorkspacePhase_Phase } from "@gitpod/public-api/lib/gitpod/v1/workspace_pb";
19+
import { workspaceClient } from "../service/public-api";
2020

2121
type WorkspaceEntryOverflowMenuProps = {
2222
info: Workspace;
@@ -42,8 +42,8 @@ export const WorkspaceEntryOverflowMenu: FunctionComponent<WorkspaceEntryOverflo
4242

4343
//TODO: shift this into ConnectToSSHModal
4444
const handleConnectViaSSHClick = useCallback(async () => {
45-
const ot = await getGitpodService().server.getOwnerToken(workspace.id);
46-
setOwnerToken(ot);
45+
const response = await workspaceClient.getWorkspaceOwnerToken({ workspaceId: workspace.id });
46+
setOwnerToken(response.ownerToken);
4747
setSSHModalVisible(true);
4848
}, [workspace.id]);
4949

components/public-api/gitpod/v1/workspace.proto

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ service WorkspaceService {
2929
// StartWorkspace starts an existing workspace.
3030
// If the specified workspace is not in stopped phase, this will return the workspace as is.
3131
rpc StartWorkspace(StartWorkspaceRequest) returns (StartWorkspaceResponse) {}
32+
33+
// GetWorkspaceDefaultImage returns the default workspace image of specified
34+
// workspace.
35+
rpc GetWorkspaceDefaultImage(GetWorkspaceDefaultImageRequest) returns (GetWorkspaceDefaultImageResponse) {}
36+
37+
// SendHeartBeat sends a heartbeat to activate the workspace
38+
rpc SendHeartBeat(SendHeartBeatRequest) returns (SendHeartBeatResponse) {}
39+
40+
// GetWorkspaceOwnerToken returns an owner token of workspace.
41+
rpc GetWorkspaceOwnerToken(GetWorkspaceOwnerTokenRequest) returns (GetWorkspaceOwnerTokenResponse) {}
42+
43+
// GetWorkspaceEditorCredentials returns an credentials that is used in editor
44+
// to encrypt and decrypt secrets
45+
rpc GetWorkspaceEditorCredentials(GetWorkspaceEditorCredentialsRequest) returns (GetWorkspaceEditorCredentialsResponse) {}
3246
}
3347

3448
message GetWorkspaceRequest {
@@ -158,6 +172,52 @@ message StartWorkspaceResponse {
158172
Workspace workspace = 1;
159173
}
160174

175+
message GetWorkspaceDefaultImageRequest {
176+
// workspace_id specifies the workspace to get default image
177+
string workspace_id = 1;
178+
}
179+
180+
message GetWorkspaceDefaultImageResponse {
181+
enum Source {
182+
SOURCE_UNSPECIFIED = 0;
183+
SOURCE_INSTALLATION = 1;
184+
SOURCE_ORGANIZATION = 2;
185+
}
186+
187+
string default_workspace_image = 1;
188+
189+
Source source = 2;
190+
}
191+
192+
message SendHeartBeatRequest {
193+
// workspace_id specifies the workspace to send heartbeat
194+
//
195+
// +required
196+
string workspace_id = 1;
197+
198+
// disconnected indicates if the editor connection is disconnected.
199+
// If set to true, the workspace will be stopped after Timeout.disconnected.
200+
bool disconnected = 2;
201+
}
202+
203+
message SendHeartBeatResponse {}
204+
205+
message GetWorkspaceOwnerTokenRequest {
206+
string workspace_id = 1;
207+
}
208+
209+
message GetWorkspaceOwnerTokenResponse {
210+
string owner_token = 1;
211+
}
212+
213+
message GetWorkspaceEditorCredentialsRequest {
214+
string workspace_id = 1;
215+
}
216+
217+
message GetWorkspaceEditorCredentialsResponse {
218+
string editor_credentials = 1;
219+
}
220+
161221
// +resource get workspace
162222
message Workspace {
163223
string id = 1;

components/public-api/go/v1/user.pb.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)