Skip to content

Commit 34992c3

Browse files
[server] snapshots for PVC workspaces (#14503)
* allow to take and check snapshot status when using pvc * [server] snapshots for PVC workspaces Co-authored-by: Pavel Tumik <[email protected]>
1 parent ed95dae commit 34992c3

File tree

8 files changed

+6332
-6847
lines changed

8 files changed

+6332
-6847
lines changed

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

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ import {
5151
} from "@gitpod/gitpod-protocol";
5252
import { ResponseError } from "vscode-jsonrpc";
5353
import {
54-
TakeSnapshotRequest,
5554
AdmissionLevel,
5655
ControlAdmissionRequest,
5756
StopWorkspacePolicy,
@@ -530,18 +529,7 @@ export class GitpodServerEEImpl extends GitpodServerImpl {
530529
}
531530
await this.guardAccess({ kind: "workspaceInstance", subject: instance, workspace }, "get");
532531

533-
const client = await this.workspaceManagerClientProvider.get(
534-
instance.region,
535-
this.config.installationShortname,
536-
);
537-
const request = new TakeSnapshotRequest();
538-
request.setId(instance.id);
539-
request.setReturnImmediately(true);
540-
541-
// this triggers the snapshots, but returns early! cmp. waitForSnapshot to wait for it's completion
542-
const resp = await client.takeSnapshot(ctx, request);
543-
544-
const snapshot = await this.snapshotService.createSnapshot(options, resp.getUrl());
532+
const snapshot = await this.snapshotService.createSnapshot(ctx, instance);
545533

546534
// to be backwards compatible during rollout, we require new clients to explicitly pass "dontWait: true"
547535
const waitOpts = { workspaceOwner: workspace.ownerId, snapshot };

components/server/ee/src/workspace/snapshot-service.ts

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@
77
import { inject, injectable } from "inversify";
88
import { v4 as uuidv4 } from "uuid";
99
import { WorkspaceDB } from "@gitpod/gitpod-db/lib";
10-
import { Disposable, GitpodServer, Snapshot } from "@gitpod/gitpod-protocol";
10+
import { Disposable, Snapshot, WorkspaceInstance } from "@gitpod/gitpod-protocol";
1111
import { StorageClient } from "../../../src/storage/storage-client";
1212
import { ConsensusLeaderQorum } from "../../../src/consensus/consensus-leader-quorum";
1313
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
1414
import { repeat } from "@gitpod/gitpod-protocol/lib/util/repeat";
15+
import { WorkspaceManagerClientProvider } from "@gitpod/ws-manager/lib/client-provider";
16+
import { GetVolumeSnapshotRequest, TakeSnapshotRequest } from "@gitpod/ws-manager/lib";
17+
import { Config } from "../../../src/config";
18+
import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing";
1519

1620
const SNAPSHOT_TIMEOUT_SECONDS = 60 * 30;
1721
const SNAPSHOT_POLL_INTERVAL_SECONDS = 5;
@@ -31,6 +35,9 @@ export class SnapshotService {
3135
@inject(WorkspaceDB) protected readonly workspaceDb: WorkspaceDB;
3236
@inject(StorageClient) protected readonly storageClient: StorageClient;
3337
@inject(ConsensusLeaderQorum) protected readonly leaderQuorum: ConsensusLeaderQorum;
38+
@inject(WorkspaceManagerClientProvider)
39+
protected readonly workspaceManagerClientProvider: WorkspaceManagerClientProvider;
40+
@inject(Config) protected readonly config: Config;
3441

3542
protected readonly runningSnapshots: Map<string, Promise<void>> = new Map();
3643

@@ -74,14 +81,24 @@ export class SnapshotService {
7481
}
7582
}
7683

77-
public async createSnapshot(options: GitpodServer.TakeSnapshotOptions, snapshotUrl: string): Promise<Snapshot> {
84+
public async createSnapshot(ctx: TraceContext, instance: WorkspaceInstance): Promise<Snapshot> {
85+
const client = await this.workspaceManagerClientProvider.get(
86+
instance.region,
87+
this.config.installationShortname,
88+
);
89+
const request = new TakeSnapshotRequest();
90+
request.setId(instance.id);
91+
request.setReturnImmediately(true);
92+
93+
// this triggers the snapshots, but returns early! cmp. waitForSnapshot to wait for it's completion
94+
const resp = await client.takeSnapshot(ctx, request);
7895
const id = uuidv4();
7996
return await this.workspaceDb.storeSnapshot({
8097
id,
8198
creationTime: new Date().toISOString(),
8299
state: "pending",
83-
bucketId: snapshotUrl,
84-
originalWorkspaceId: options.workspaceId,
100+
bucketId: resp.getUrl(),
101+
originalWorkspaceId: instance.workspaceId,
85102
});
86103
}
87104

@@ -112,6 +129,24 @@ export class SnapshotService {
112129

113130
const { id: snapshotId, bucketId, originalWorkspaceId, creationTime } = opts.snapshot;
114131
const start = new Date(creationTime).getTime();
132+
const workspace = await this.workspaceDb.findWorkspaceAndInstance(originalWorkspaceId);
133+
if (!workspace) {
134+
const message = `Couldn't find original workspace for snapshot.`;
135+
await this.workspaceDb.updateSnapshot({
136+
id: snapshotId,
137+
state: "error",
138+
message,
139+
});
140+
throw new Error(message);
141+
}
142+
const client = await this.workspaceManagerClientProvider.get(
143+
workspace.region,
144+
this.config.installationShortname,
145+
);
146+
const req = new GetVolumeSnapshotRequest();
147+
req.setId(workspace.instanceId);
148+
149+
const isPVC = workspace?.config._featureFlags?.some((f) => f === "persistent_volume_claim");
115150
while (start + SNAPSHOT_TIMEOUT_SECONDS * 1000 > Date.now()) {
116151
await new Promise((resolve) => setTimeout(resolve, SNAPSHOT_POLL_INTERVAL_SECONDS * 1000));
117152

@@ -126,13 +161,19 @@ export class SnapshotService {
126161
if (snapshot.state === "error") {
127162
throw new Error(`snapshot error: ${snapshot.message}`);
128163
}
164+
let exists = false;
165+
if (isPVC) {
166+
const response = await client.getVolumeSnapshot({}, req);
167+
exists = response.getReady();
168+
} else {
169+
// pending: check if the snapshot is there
170+
exists = await this.storageClient.workspaceSnapshotExists(
171+
opts.workspaceOwner,
172+
originalWorkspaceId,
173+
bucketId,
174+
);
175+
}
129176

130-
// pending: check if the snapshot is there
131-
const exists = await this.storageClient.workspaceSnapshotExists(
132-
opts.workspaceOwner,
133-
originalWorkspaceId,
134-
bucketId,
135-
);
136177
if (exists) {
137178
await this.workspaceDb.updateSnapshot({
138179
id: snapshotId,

components/ws-manager-api/go/mock/mock.go

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)