Skip to content

[server] Merge WorkspaceDeletionService into WorkspaceGC #18410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions components/dashboard/src/service/service-mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
*/

import { createServiceMock, Event, Project, Team, User } from "@gitpod/gitpod-protocol";
import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode";

const u1: User = {
id: "1234",
Expand Down Expand Up @@ -254,9 +253,6 @@ const gitpodServiceMock = createServiceMock({
},
};
},
getBillingModeForUser: async () => {
return BillingMode.NONE;
},
});

export { gitpodServiceMock };
1 change: 0 additions & 1 deletion components/gitpod-protocol/src/gitpod-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,6 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,

listUsage(req: ListUsageRequest): Promise<ListUsageResponse>;

getBillingModeForUser(): Promise<BillingMode>;
getBillingModeForTeam(teamId: string): Promise<BillingMode>;

getLinkedInClientId(): Promise<string>;
Expand Down
1 change: 0 additions & 1 deletion components/server/src/auth/rate-limiter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ const defaultFunctions: FunctionsConfig = {
getPriceInformation: { group: "default", points: 1 },
listUsage: { group: "default", points: 1 },
getBillingModeForTeam: { group: "default", points: 1 },
getBillingModeForUser: { group: "default", points: 1 },
getLinkedInClientId: { group: "default", points: 1 },
connectWithLinkedIn: { group: "default", points: 1 },

Expand Down
9 changes: 6 additions & 3 deletions components/server/src/billing/billing-mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import { inject, injectable } from "inversify";

import { User } from "@gitpod/gitpod-protocol";
import { Config } from "../config";
import { BillingMode } from "@gitpod/gitpod-protocol/lib/billing-mode";
import { CostCenter_BillingStrategy } from "@gitpod/usage-api/lib/usage/v1/usage.pb";
Expand All @@ -22,7 +21,7 @@ export class BillingModes {
@inject(UsageService) private readonly usageService: UsageService,
) {}

public async getBillingMode(userId: string, organizationId: string, now: Date): Promise<BillingMode> {
public async getBillingMode(userId: string, organizationId: string): Promise<BillingMode> {
if (!this.config.enablePayment) {
// Payment is not enabled. E.g. Dedicated
return { mode: "none" };
Expand All @@ -33,7 +32,11 @@ export class BillingModes {
return { mode: "usage-based", paid };
}

async getBillingModeForUser(user: User, now: Date): Promise<BillingMode> {
/**
* @deprecated use getBillingMode(userId, organizationId) instead
* @returns
*/
async getBillingModeForUser(): Promise<BillingMode> {
if (!this.config.enablePayment) {
// Payment is not enabled. E.g. Self-Hosted.
return { mode: "none" };
Expand Down
61 changes: 27 additions & 34 deletions components/server/src/billing/entitlement-service-ubp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@

import { TeamDB } from "@gitpod/gitpod-db/lib";
import {
BillingTier,
Team,
User,
WorkspaceInstance,
WorkspaceTimeoutDuration,
WORKSPACE_TIMEOUT_DEFAULT_LONG,
WORKSPACE_TIMEOUT_DEFAULT_SHORT,
WORKSPACE_LIFETIME_LONG,
WORKSPACE_LIFETIME_SHORT,
User,
BillingTier,
Team,
} from "@gitpod/gitpod-protocol";
import { AttributionId } from "@gitpod/gitpod-protocol/lib/attribution";
import { inject, injectable } from "inversify";
Expand All @@ -38,11 +38,10 @@ export class EntitlementServiceUBP implements EntitlementService {
async mayStartWorkspace(
user: User,
organizationId: string,
date: Date,
runningInstances: Promise<WorkspaceInstance[]>,
): Promise<MayStartWorkspaceResult> {
const hasHitParallelWorkspaceLimit = async (): Promise<HitParallelWorkspaceLimit | undefined> => {
const max = await this.getMaxParallelWorkspaces(user, date);
const max = await this.getMaxParallelWorkspaces(user.id, organizationId);
const current = (await runningInstances).filter((i) => i.status.phase !== "preparing").length;
if (current >= max) {
return {
Expand All @@ -54,7 +53,7 @@ export class EntitlementServiceUBP implements EntitlementService {
}
};
const [usageLimitReachedOnCostCenter, hitParallelWorkspaceLimit] = await Promise.all([
this.checkUsageLimitReached(user, organizationId, date),
this.checkUsageLimitReached(user.id, organizationId),
hasHitParallelWorkspaceLimit(),
]);
return {
Expand All @@ -63,69 +62,63 @@ export class EntitlementServiceUBP implements EntitlementService {
};
}

private async checkUsageLimitReached(
user: User,
organizationId: string,
date: Date,
): Promise<AttributionId | undefined> {
const result = await this.usageService.checkUsageLimitReached(user.id, organizationId);
private async checkUsageLimitReached(userId: string, organizationId: string): Promise<AttributionId | undefined> {
const result = await this.usageService.checkUsageLimitReached(userId, organizationId);
if (result.reached) {
return result.attributionId;
}
return undefined;
}

private async getMaxParallelWorkspaces(user: User, date: Date): Promise<number> {
if (await this.hasPaidSubscription(user, date)) {
private async getMaxParallelWorkspaces(userId: string, organizationId: string): Promise<number> {
if (await this.hasPaidSubscription(userId, organizationId)) {
return MAX_PARALLEL_WORKSPACES_PAID;
} else {
return MAX_PARALLEL_WORKSPACES_FREE;
}
}

async maySetTimeout(user: User, date: Date): Promise<boolean> {
return this.hasPaidSubscription(user, date);
async maySetTimeout(userId: string, organizationId?: string): Promise<boolean> {
return this.hasPaidSubscription(userId, organizationId);
}

async getDefaultWorkspaceTimeout(user: User, date: Date): Promise<WorkspaceTimeoutDuration> {
if (await this.hasPaidSubscription(user, date)) {
async getDefaultWorkspaceTimeout(userId: string, organizationId: string): Promise<WorkspaceTimeoutDuration> {
if (await this.hasPaidSubscription(userId, organizationId)) {
return WORKSPACE_TIMEOUT_DEFAULT_LONG;
} else {
return WORKSPACE_TIMEOUT_DEFAULT_SHORT;
}
}

async getDefaultWorkspaceLifetime(user: User, date: Date): Promise<WorkspaceTimeoutDuration> {
if (await this.hasPaidSubscription(user, date)) {
async getDefaultWorkspaceLifetime(userId: string, organizationId: string): Promise<WorkspaceTimeoutDuration> {
if (await this.hasPaidSubscription(userId, organizationId)) {
return WORKSPACE_LIFETIME_LONG;
} else {
return WORKSPACE_LIFETIME_SHORT;
}
}

/**
* DEPRECATED: With usage-based billing, users can choose exactly how many resources they want to get.
* Thus, we no longer need to "force" extra resources via the `userGetsMoreResources` mechanism.
*/
async userGetsMoreResources(user: User, date: Date = new Date()): Promise<boolean> {
return false;
}

/**
* Returns true if network connections should be limited
* @param user
*/
async limitNetworkConnections(user: User, date: Date): Promise<boolean> {
async limitNetworkConnections(userId: string, organizationId: string): Promise<boolean> {
// gpl: Because with the current payment handling (pay-after-use) having a "paid" plan is not a good enough classifier for trushworthyness atm.
// We're looking into improving this, but for the meantime we limit network connections for everybody to reduce the impact of abuse.
return true;
}

private async hasPaidSubscription(user: User, date: Date): Promise<boolean> {
private async hasPaidSubscription(userId: string, organizationId?: string): Promise<boolean> {
if (organizationId) {
// This is the "stricter", more correct version: We only allow privileges on the Organization that is paying for it
const { billingStrategy } = await this.usageService.getCostCenter(userId, organizationId);
return billingStrategy === CostCenter_BillingStrategy.BILLING_STRATEGY_STRIPE;
}
// This is the old behavior, stemming from our transition to PAYF, where our API did-/doesn't pass organizationId, yet
// Member of paid team?
const teams = await this.teamDB.findTeamsByUser(user.id);
const teams = await this.teamDB.findTeamsByUser(userId);
const isTeamSubscribedPromises = teams.map(async (team: Team) => {
const { billingStrategy } = await this.usageService.getCostCenter(user.id, team.id);
const { billingStrategy } = await this.usageService.getCostCenter(userId, team.id);
return billingStrategy === CostCenter_BillingStrategy.BILLING_STRATEGY_STRIPE;
});
// Return the first truthy promise, or false if all the promises were falsy.
Expand All @@ -147,8 +140,8 @@ export class EntitlementServiceUBP implements EntitlementService {
});
}

async getBillingTier(user: User): Promise<BillingTier> {
const hasPaidPlan = await this.hasPaidSubscription(user, new Date());
async getBillingTier(userId: string, organizationId: string): Promise<BillingTier> {
const hasPaidPlan = await this.hasPaidSubscription(userId, organizationId);
return hasPaidPlan ? "paid" : "free";
}
}
Loading