Skip to content

Commit

Permalink
[server] remove prebuilt time limit
Browse files Browse the repository at this point in the history
  • Loading branch information
svenefftinge committed Sep 13, 2021
1 parent b5b5d53 commit 502f4b8
Show file tree
Hide file tree
Showing 13 changed files with 12 additions and 149 deletions.
Expand Up @@ -6,7 +6,7 @@

import { MigrationInterface, QueryRunner } from "typeorm";

import { BUILTIN_WORKSPACE_PROBE_USER_NAME } from "../../user-db";
import { BUILTIN_WORKSPACE_PROBE_USER_ID } from "../../user-db";

export class Baseline1592203031938 implements MigrationInterface {

Expand Down Expand Up @@ -81,7 +81,7 @@ export class Baseline1592203031938 implements MigrationInterface {
{
const exists = (await queryRunner.query(`SELECT COUNT(1) AS cnt FROM d_b_user WHERE id = 'builtin-user-workspace-probe-0000000'`))[0].cnt == 1;
if (!exists) {
await queryRunner.query(`INSERT IGNORE INTO d_b_user (id, creationDate, avatarUrl, name, fullName) VALUES ('builtin-user-workspace-probe-0000000', '${new Date().toISOString()}', '', '${BUILTIN_WORKSPACE_PROBE_USER_NAME}', '')`)
await queryRunner.query(`INSERT IGNORE INTO d_b_user (id, creationDate, avatarUrl, name, fullName) VALUES ('${BUILTIN_WORKSPACE_PROBE_USER_ID}', '${new Date().toISOString()}', '', 'builtin-workspace-prober', '')`)
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions components/gitpod-db/src/typeorm/user-db-impl.ts
Expand Up @@ -11,7 +11,7 @@ import { DateInterval, ExtraAccessTokenFields, GrantIdentifier, OAuthClient, OAu
import { inject, injectable, postConstruct } from "inversify";
import { EntityManager, Repository } from "typeorm";
import * as uuidv4 from 'uuid/v4';
import { BUILTIN_WORKSPACE_PROBE_USER_NAME, MaybeUser, PartialUserUpdate, UserDB } from "../user-db";
import { BUILTIN_WORKSPACE_PROBE_USER_ID, MaybeUser, PartialUserUpdate, UserDB } from "../user-db";
import { DBGitpodToken } from "./entity/db-gitpod-token";
import { DBIdentity } from "./entity/db-identity";
import { DBTokenEntry } from "./entity/db-token-entry";
Expand Down Expand Up @@ -310,7 +310,7 @@ export class TypeORMUserDBImpl implements UserDB {
WHERE markedDeleted != true`;
if (excludeBuiltinUsers) {
query = `${query}
AND name <> '${BUILTIN_WORKSPACE_PROBE_USER_NAME}'`
AND id <> '${BUILTIN_WORKSPACE_PROBE_USER_ID}'`
}
const res = await userRepo.query(query);
const count = res[0].cnt;
Expand Down Expand Up @@ -354,7 +354,7 @@ export class TypeORMUserDBImpl implements UserDB {
qBuilder.andWhere("user.creationDate < :maxCreationDate", { maxCreationDate: maxCreationDate.toISOString() });
}
if (excludeBuiltinUsers) {
qBuilder.andWhere("user.name <> :username", { username: BUILTIN_WORKSPACE_PROBE_USER_NAME })
qBuilder.andWhere("user.id <> :userId", { userId: BUILTIN_WORKSPACE_PROBE_USER_ID })
}
qBuilder.orderBy("user." + orderBy, orderDir);
qBuilder.skip(offset).take(limit).select();
Expand Down
27 changes: 3 additions & 24 deletions components/gitpod-db/src/typeorm/workspace-db-impl.ts
Expand Up @@ -18,7 +18,7 @@ import { DBRepositoryWhiteList } from "./entity/db-repository-whitelist";
import { log } from '@gitpod/gitpod-protocol/lib/util/logging';
import { DBPrebuiltWorkspace } from "./entity/db-prebuilt-workspace";
import { DBPrebuiltWorkspaceUpdatable } from "./entity/db-prebuilt-workspace-updatable";
import { BUILTIN_WORKSPACE_PROBE_USER_NAME } from "../user-db";
import { BUILTIN_WORKSPACE_PROBE_USER_ID } from "../user-db";
import { DBPrebuildInfo } from "./entity/db-prebuild-info-entry";

type RawTo<T> = (instance: WorkspaceInstance, ws: Workspace) => T;
Expand Down Expand Up @@ -463,11 +463,9 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB {
ws.softDeletedTime < NOW() - INTERVAL ? DAY
OR ws.softDeletedTime = ''
)
AND ws.ownerId NOT IN (
SELECT id from d_b_user WHERE name = ?
)
AND ws.ownerId <> ?
LIMIT ?;
`, [minSoftDeletedTimeInDays, BUILTIN_WORKSPACE_PROBE_USER_NAME, limit]);
`, [minSoftDeletedTimeInDays, BUILTIN_WORKSPACE_PROBE_USER_ID, limit]);

return dbResults as WorkspaceOwnerAndSoftDeleted[];
}
Expand Down Expand Up @@ -574,25 +572,6 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB {
.getOne();
}

public async getTotalPrebuildUseSeconds(forDays: number): Promise<number | undefined> {
const manager = await this.getManager();
const res = await manager.query(`
SELECT SUM(COALESCE(STR_TO_DATE(wsi.stoppedTime, "%Y-%m-%dT%H:%i:%s.%fZ") - STR_TO_DATE(wsi.startedTime, "%Y-%m-%dT%H:%i:%s.%fZ"))) AS tps FROM d_b_workspace_instance wsi
INNER JOIN d_b_prebuilt_workspace pws ON pws.buildWorkspaceId = wsi.workspaceId
INNER JOIN (
SELECT ws.context->>'$.prebuildWorkspaceId' as sid FROM d_b_workspace ws
WHERE ws.creationTime > NOW() - INTERVAL ? DAY
AND JSON_EXTRACT(ws.context, '$.prebuildWorkspaceId') IS NOT NULL
) AS sns ON sns.sid = pws.id
WHERE wsi.startedTime IS NOT NULL
`, [forDays]);
if (!res || res.length < 1) {
return;
}

return res[0].tps;
}

public async findPrebuildByWorkspaceID(wsid: string): Promise<PrebuiltWorkspace | undefined> {
const repo = await this.getPrebuiltWorkspaceRepo();
return await repo.createQueryBuilder('pws')
Expand Down
2 changes: 1 addition & 1 deletion components/gitpod-db/src/user-db.ts
Expand Up @@ -119,7 +119,7 @@ export interface UserDB extends OAuthUserRepository, OAuthTokenRepository {
}
export type PartialUserUpdate = Partial<Omit<User, "identities">> & Pick<User, "id">

export const BUILTIN_WORKSPACE_PROBE_USER_NAME = "builtin-workspace-prober";
export const BUILTIN_WORKSPACE_PROBE_USER_ID = "builtin-user-workspace-probe-0000000";

export interface OwnerAndRepo {
owner: string
Expand Down
1 change: 0 additions & 1 deletion components/gitpod-db/src/workspace-db.ts
Expand Up @@ -95,7 +95,6 @@ export interface WorkspaceDB {
findSnapshotsByWorkspaceId(workspaceId: string): Promise<Snapshot[]>;
storeSnapshot(snapshot: Snapshot): Promise<Snapshot>;

getTotalPrebuildUseSeconds(forDays: number): Promise<number | undefined>;
storePrebuiltWorkspace(pws: PrebuiltWorkspace): Promise<PrebuiltWorkspace>;
findPrebuiltWorkspaceByCommit(cloneURL: string, commit: string): Promise<PrebuiltWorkspace | undefined>;
findPrebuildsWithWorkpace(cloneURL: string): Promise<PrebuildWithWorkspace[]>;
Expand Down
21 changes: 0 additions & 21 deletions components/licensor/ee/pkg/licensor/licensor.go
Expand Up @@ -218,27 +218,6 @@ func (e *Evaluator) HasEnoughSeats(seats int) bool {
return e.lic.Seats == 0 || seats <= e.lic.Seats
}

// CanUsePrebuild returns true if the use a prebuild is permissible under the license,
// given the total prebuild time used already.
func (e *Evaluator) CanUsePrebuild(totalPrebuildTimeSpent time.Duration) bool {
if !e.Enabled(FeaturePrebuild) {
// prebuilds aren't even enabled - time spent doesn't matter
return false
}

pbt := e.lic.Level.allowance().PrebuildTime
if pbt == 0 {
// allowed prebuild time == 0 means the prebuild time is not limited
return true
}
if totalPrebuildTimeSpent <= pbt {
// not all time is spent yet
return true
}

return false
}

// Inspect returns the license information this evaluator holds.
// This function is intended for transparency/debugging purposes only and must
// never be used to determine feature eligibility under a license. All code making
Expand Down
24 changes: 0 additions & 24 deletions components/licensor/ee/pkg/licensor/licensor_test.go
Expand Up @@ -151,30 +151,6 @@ func TestFeatures(t *testing.T) {
}
}

func TestCanUsePrebuild(t *testing.T) {
validate := func(usedTime time.Duration, expectation bool) func(t *testing.T, eval *Evaluator) {
return func(t *testing.T, eval *Evaluator) {
act := eval.CanUsePrebuild(usedTime)
if expectation != act {
t.Errorf("CanUsePrebuild returned unexpected value: expected %v, got %v", expectation, act)
}
}
}

enterpriseLic := &LicensePayload{Domain: domain, ID: someID, Level: LevelEnterprise, Seats: 0, ValidUntil: time.Now().Add(6 * time.Hour)}
tests := []licenseTest{
{Name: "default license ok", License: nil, Validate: validate(0*time.Hour, true)},
{Name: "default license not ok", License: nil, Validate: validate(250*time.Hour, false)},
{Name: "enterprise license a", License: enterpriseLic, Validate: validate(1*time.Hour, true)},
{Name: "enterprise license b", License: enterpriseLic, Validate: validate(500*time.Hour, true)},
{Name: "enterprise license c", License: enterpriseLic, Validate: validate(-1*time.Hour, true)},
{Name: "broken license", License: &LicensePayload{Level: LevelEnterprise}, Validate: validate(0*time.Hour, false)},
}
for _, test := range tests {
test.Run(t)
}
}

func TestEvalutorKeys(t *testing.T) {
tests := []struct {
Name string
Expand Down
13 changes: 0 additions & 13 deletions components/licensor/typescript/ee/main.go
Expand Up @@ -7,7 +7,6 @@ package main
import (
"C"
"encoding/json"
"time"

log "github.com/sirupsen/logrus"

Expand Down Expand Up @@ -63,18 +62,6 @@ func HasEnoughSeats(id int, seats int) (permitted, ok bool) {
return e.HasEnoughSeats(seats), true
}

// CanUsePrebuild returns true if the use a prebuild is permissible under the license,
// given the total prebuild time used already.
//export CanUsePrebuild
func CanUsePrebuild(id int, totalSecondsUsed int64) (permitted, ok bool) {
e, ok := instances[id]
if !ok {
return
}

return e.CanUsePrebuild(time.Duration(totalSecondsUsed) * time.Second), true
}

// Inspect returns the license information this evaluator holds.
//export Inspect
func Inspect(id int) (lic *C.char, ok bool) {
Expand Down
6 changes: 1 addition & 5 deletions components/licensor/typescript/ee/src/index.ts
Expand Up @@ -5,7 +5,7 @@
*/

import { injectable, inject, postConstruct } from 'inversify';
import { init, Instance, dispose, isEnabled, hasEnoughSeats, canUsePrebuild, inspect, validate } from "./nativemodule";
import { init, Instance, dispose, isEnabled, hasEnoughSeats, inspect, validate } from "./nativemodule";
import { Feature, LicensePayload } from './api';

export const LicenseKeySource = Symbol("LicenseKeySource");
Expand Down Expand Up @@ -57,10 +57,6 @@ export class LicenseEvaluator {
return hasEnoughSeats(this.instanceID, seats);
}

public canUsePrebuild(totalPrebuildSecondsSpent: number): boolean {
return canUsePrebuild(this.instanceID, totalPrebuildSecondsSpent);
}

public inspect(): LicensePayload {
return JSON.parse(inspect(this.instanceID));
}
Expand Down
36 changes: 0 additions & 36 deletions components/licensor/typescript/ee/src/module.cc
Expand Up @@ -165,41 +165,6 @@ void HasEnoughSeatsM(const FunctionCallbackInfo<Value> &args) {
args.GetReturnValue().Set(Boolean::New(isolate, r.r0));
}

void CanUsePrebuildM(const FunctionCallbackInfo<Value> &args) {
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();

if (args.Length() < 2) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "wrong number of arguments")));
return;
}
if (!args[0]->IsNumber() || args[0]->IsUndefined()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 0 must be a number")));
return;
}
if (!args[1]->IsNumber() || args[1]->IsUndefined()) {
isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "argument 1 must be a number")));
return;
}

double rid = args[0]->NumberValue(context).FromMaybe(0);
int id = static_cast<int>(rid);
double rsec = args[1]->NumberValue(context).FromMaybe(-1);
GoInt64 sec = static_cast<GoInt64>(rsec);
if (sec < 0) {
isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "cannot convert total prebuild time used")));
}

CanUsePrebuild_return r = CanUsePrebuild(id, sec);
if (!r.r1) {
isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "invalid instance ID")));
return;
}

// return the value
args.GetReturnValue().Set(Boolean::New(isolate, r.r0));
}

void InspectM(const FunctionCallbackInfo<Value> &args) {
Isolate *isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Expand Down Expand Up @@ -250,7 +215,6 @@ void initModule(Local<Object> exports) {
NODE_SET_METHOD(exports, "validate", ValidateM);
NODE_SET_METHOD(exports, "isEnabled", EnabledM);
NODE_SET_METHOD(exports, "hasEnoughSeats", HasEnoughSeatsM);
NODE_SET_METHOD(exports, "canUsePrebuild", CanUsePrebuildM);
NODE_SET_METHOD(exports, "inspect", InspectM);
NODE_SET_METHOD(exports, "dispose", DisposeM);
}
Expand Down
1 change: 0 additions & 1 deletion components/licensor/typescript/ee/src/nativemodule.d.ts
Expand Up @@ -11,6 +11,5 @@ export function init(key: string, domain: string): Instance;
export function validate(id: Instance): { msg: string, valid: boolean };
export function isEnabled(id: Instance, feature: Feature): boolean;
export function hasEnoughSeats(id: Instance, seats: int): boolean;
export function canUsePrebuild(id: Instance, totalPrebuildTimeSpent: number): boolean;
export function inspect(id: Instance): string;
export function dispose(id: Instance);
16 changes: 0 additions & 16 deletions components/server/ee/src/workspace/workspace-factory.ts
Expand Up @@ -192,11 +192,6 @@ export class WorkspaceFactoryEE extends WorkspaceFactory {
this.requireEELicense(Feature.FeaturePrebuild);
const span = TraceContext.startSpan("createForPrebuiltWorkspace", ctx);

const fallback = await this.fallbackIfOutPrebuildTime(ctx, user, context, normalizedContextURL);
if (!!fallback) {
return fallback;
}

try {
const buildWorkspaceID = context.prebuiltWorkspace.buildWorkspaceId;
const buildWorkspace = await this.db.trace({span}).findById(buildWorkspaceID);
Expand Down Expand Up @@ -241,15 +236,4 @@ export class WorkspaceFactoryEE extends WorkspaceFactory {
}
}

protected async fallbackIfOutPrebuildTime(ctx: TraceContext, user: User, context: PrebuiltWorkspaceContext, normalizedContextURL: string): Promise<Workspace | undefined> {
const prebuildTime = await this.db.trace({}).getTotalPrebuildUseSeconds(30);
if (!this.licenseEvaluator.canUsePrebuild(prebuildTime || 0)) {
// TODO: find a way to signal the out-of-prebuild-time situation
log.warn({}, "cannot use prebuild because enterprise license prevents it", {prebuildTime});
return this.createForContext(ctx, user, context.originalContext, normalizedContextURL);
}

return;
}

}
Expand Up @@ -10,7 +10,7 @@ import { WorkspaceInstance, WorkspaceProbeContext, RunningWorkspaceInfo } from "
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
import { URL } from "url";
import { WorkspaceFactory } from "../../../src/workspace/workspace-factory";
import { UserDB, BUILTIN_WORKSPACE_PROBE_USER_NAME, WorkspaceDB, TracedWorkspaceDB, DBWithTracing, TracedUserDB } from "@gitpod/gitpod-db/lib";
import { UserDB, BUILTIN_WORKSPACE_PROBE_USER_ID, WorkspaceDB, TracedWorkspaceDB, DBWithTracing, TracedUserDB } from "@gitpod/gitpod-db/lib";
import { WorkspaceStarter } from "../../../src/workspace/workspace-starter";
import fetch from "node-fetch";
import { Config } from "../../../src/config";
Expand All @@ -37,7 +37,7 @@ export class WorkspaceHealthMonitoring {
const span = TraceContext.startSpan("startWorkspaceProbe", ctx);

try {
const user = await this.userDB.trace({span}).findUserByName(BUILTIN_WORKSPACE_PROBE_USER_NAME);
const user = await this.userDB.trace({span}).findUserById(BUILTIN_WORKSPACE_PROBE_USER_ID);
if (!user) {
throw new Error("cannot find workspace probe user. DB not set up properly?")
}
Expand Down

0 comments on commit 502f4b8

Please sign in to comment.