Skip to content

Commit 0d85286

Browse files
authored
[server] migrate ws without usageattribution (#17485)
1 parent f77c236 commit 0d85286

File tree

3 files changed

+99
-4
lines changed

3 files changed

+99
-4
lines changed

components/gitpod-db/src/user-to-team-migration-service.spec.db.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,4 +158,41 @@ describe("Migration Service", () => {
158158
const teams = await teamDB.findTeamsByUser(user.id);
159159
expect(teams[0].name).to.be.eq("X Organization");
160160
});
161+
162+
it("should update 'organizationId' for workspace without attributionId", async () => {
163+
await wipeRepo();
164+
const user = await userDB.newUser();
165+
await userDB.storeUser(user);
166+
167+
const ws = await workspaceDB.store({
168+
id: uuidv4(),
169+
creationTime: new Date().toISOString(),
170+
ownerId: user.id,
171+
config: {},
172+
context: {
173+
title: "test",
174+
},
175+
contextURL: "https://gitpod.io",
176+
type: "regular",
177+
description: "test",
178+
});
179+
180+
await workspaceDB.storeInstance({
181+
id: uuidv4(),
182+
creationTime: new Date().toISOString(),
183+
region: "eu-west-1",
184+
ideUrl: "https://ide.eu-west-1.aws.com",
185+
workspaceImage: "test",
186+
status: {
187+
conditions: {},
188+
phase: "stopped",
189+
},
190+
workspaceId: ws.id,
191+
});
192+
193+
await migrationService.migrateUser(user);
194+
const wsAndI = await workspaceDB.findWorkspaceAndInstance(ws.id);
195+
const teams = await teamDB.findTeamsByUser(user.id);
196+
expect(wsAndI?.organizationId).to.be.eq(teams[0].id);
197+
});
161198
});

components/gitpod-db/src/user-to-team-migration-service.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { AdditionalUserData, Team, User } from "@gitpod/gitpod-protocol";
7+
import { AdditionalUserData, Team, User, WorkspaceInfo } from "@gitpod/gitpod-protocol";
88
import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
99
import { inject, injectable } from "inversify";
1010
import { ProjectDB } from "./project-db";
@@ -15,6 +15,7 @@ import { TypeORM } from "./typeorm/typeorm";
1515
import { log, LogContext } from "@gitpod/gitpod-protocol/lib/util/logging";
1616
import { UserDB } from "./user-db";
1717
import { Synchronizer } from "./typeorm/synchronizer";
18+
import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution";
1819

1920
@injectable()
2021
export class UserToTeamMigrationService {
@@ -135,6 +136,13 @@ export class UserToTeamMigrationService {
135136
);
136137
log.info(ctx, "Migrated workspaces.", { teamId: team.id, result });
137138

139+
// Ensure there are no workspaces without an organizationId. This is necessary because very old workspace instance don't have an attributionId.
140+
const workspaces = await this.workspaceDB.find({
141+
userId: user.id,
142+
});
143+
await this.updateWorkspacesOrganizationId(workspaces, team.id);
144+
log.info(ctx, "Updated workspaces.", { teamId: team.id });
145+
138146
result = await conn.query("UPDATE d_b_usage SET attributionId = ? WHERE attributionId = ?", [
139147
newAttribution,
140148
oldAttribution,
@@ -153,4 +161,30 @@ export class UserToTeamMigrationService {
153161
const teams = await this.teamDB.findTeamsByUser(user.id);
154162
return teams.length === 0 || !user.additionalData?.isMigratedToTeamOnlyAttribution;
155163
}
164+
165+
async updateWorkspacesOrganizationId(workspaces: WorkspaceInfo[], userOrgId: string): Promise<WorkspaceInfo[]> {
166+
return await Promise.all(
167+
workspaces.map(async (ws) => {
168+
if (!ws.workspace.organizationId) {
169+
const attrId =
170+
ws.latestInstance?.usageAttributionId &&
171+
AttributionId.parse(ws.latestInstance.usageAttributionId);
172+
if (attrId && attrId.kind === "team") {
173+
ws.workspace.organizationId = attrId.teamId;
174+
} else {
175+
ws.workspace.organizationId = userOrgId;
176+
}
177+
await this.workspaceDB.updatePartial(ws.workspace.id, {
178+
organizationId: ws.workspace.organizationId,
179+
});
180+
}
181+
return ws;
182+
}),
183+
);
184+
}
185+
186+
async getUserOrganization(user: User): Promise<Team> {
187+
const teams = await this.teamDB.findTeamsByUser(user.id);
188+
return teams.find((t) => t.name === user.name || t.name === user.fullName) || teams[0];
189+
}
156190
}

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

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,6 +1206,17 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
12061206
),
12071207
),
12081208
);
1209+
1210+
// we need to update old workspaces on the fly that didn't get an orgId because we lack attribution on their instances.
1211+
// this can be removed eventually.
1212+
if (user.additionalData?.isMigratedToTeamOnlyAttribution) {
1213+
try {
1214+
const userOrg = await this.userToTeamMigrationService.getUserOrganization(user);
1215+
await this.userToTeamMigrationService.updateWorkspacesOrganizationId(res, userOrg.id);
1216+
} catch (error) {
1217+
log.error({ userId: user.id }, "Error updating workspaces without orgId.", error);
1218+
}
1219+
}
12091220
return res;
12101221
}
12111222

@@ -1292,11 +1303,24 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
12921303
}
12931304

12941305
protected async internalGetWorkspace(id: string, db: WorkspaceDB): Promise<Workspace> {
1295-
const ws = await db.findById(id);
1296-
if (!ws) {
1306+
const workspace = await db.findById(id);
1307+
if (!workspace) {
12971308
throw new ResponseError(ErrorCodes.NOT_FOUND, "Workspace not found.");
12981309
}
1299-
return ws;
1310+
if (!workspace.organizationId && this.user?.additionalData?.isMigratedToTeamOnlyAttribution) {
1311+
try {
1312+
log.info({ userId: this.user.id }, "Updating workspace without orgId.");
1313+
const userOrg = await this.userToTeamMigrationService.getUserOrganization(this.user);
1314+
const latestInstance = await this.workspaceDb.trace({}).findCurrentInstance(workspace.id);
1315+
this.userToTeamMigrationService.updateWorkspacesOrganizationId(
1316+
[{ workspace, latestInstance }],
1317+
userOrg.id,
1318+
);
1319+
} catch (error) {
1320+
log.error({ userId: this.user.id }, "Error updating workspaces without orgId.", error);
1321+
}
1322+
}
1323+
return workspace;
13001324
}
13011325

13021326
private async findRunningInstancesForContext(

0 commit comments

Comments
 (0)