Skip to content

Commit 5f6114c

Browse files
Pothulapatiroboquat
authored andcommitted
telemetry: add more fields to data
Fixes #7866 This PR updates the `installation-admin-controller` to also retrieve more data to send with telemetry. These are not part of the `installationAdminDb` as we do not want to store this in the database but lazily retrieve whenever a request is sent to `/data` endpoint of the `installation-admin` express app unlike the `uuid` and settings which need to be stored and updated. The following fields are added: - `totalUsers` : specifies the total number of users in the instance - `totalWorkspaces`: specifies the total number of **regular** workspaces in the instance - `totalInstances`: specifies the total number of **regular** workspace instances in the gitpod instance Signed-off-by: Tarun Pothulapati <[email protected]>
1 parent a3d1b61 commit 5f6114c

File tree

6 files changed

+60
-17
lines changed

6 files changed

+60
-17
lines changed

components/gitpod-db/src/typeorm/workspace-db-impl.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB {
158158
.addOrderBy('GREATEST(ws.creationTime, wsi.creationTime, wsi.startedTime, wsi.stoppedTime)', 'DESC')
159159
.limit(options.limit || 10);
160160
if (options.searchString) {
161-
qb.andWhere("ws.description LIKE :searchString", {searchString: `%${options.searchString}%`});
161+
qb.andWhere("ws.description LIKE :searchString", { searchString: `%${options.searchString}%` });
162162
}
163163
if (!options.includeHeadless) {
164164
qb.andWhere("ws.type = 'regular'");
@@ -334,6 +334,15 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB {
334334
return { total, rows };
335335
}
336336

337+
public async getInstanceCount(type?: string): Promise<number> {
338+
const workspaceInstanceRepo = await this.getWorkspaceInstanceRepo();
339+
const queryBuilder = workspaceInstanceRepo.createQueryBuilder("wsi")
340+
.leftJoinAndMapOne("wsi.workspace", DBWorkspace, "ws", "wsi.workspaceId = ws.id")
341+
.where("ws.type = :type", { type: type ? type.toString() : "regular" }); // only regular workspaces by default
342+
343+
return await queryBuilder.getCount();
344+
}
345+
337346
public async findRegularRunningInstances(userId?: string): Promise<WorkspaceInstance[]> {
338347
const infos = await this.findRunningInstancesWithWorkspaces(undefined, userId);
339348
return infos.filter(
@@ -578,7 +587,7 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB {
578587

579588
public async findSnapshotsByWorkspaceId(workspaceId: string): Promise<Snapshot[]> {
580589
const snapshots = await this.getSnapshotRepo();
581-
return snapshots.find({where: {originalWorkspaceId: workspaceId}});
590+
return snapshots.find({ where: { originalWorkspaceId: workspaceId } });
582591
}
583592

584593
public async storePrebuiltWorkspace(pws: PrebuiltWorkspace): Promise<PrebuiltWorkspace> {
@@ -596,7 +605,7 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB {
596605
}
597606
const repo = await this.getPrebuiltWorkspaceRepo();
598607
return await repo.createQueryBuilder('pws')
599-
.where('pws.cloneURL = :cloneURL AND pws.commit LIKE :commit', { cloneURL, commit: commit+'%' })
608+
.where('pws.cloneURL = :cloneURL AND pws.commit LIKE :commit', { cloneURL, commit: commit + '%' })
600609
.orderBy('pws.creationTime', 'DESC')
601610
.innerJoinAndMapOne('pws.workspace', DBWorkspace, 'ws', "pws.buildWorkspaceId = ws.id and ws.contentDeletedTime = ''")
602611
.getOne();
@@ -737,7 +746,13 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB {
737746
return { total, rows };
738747
}
739748

749+
public async getWorkspaceCount(type?: String): Promise<Number> {
750+
const workspaceRepo = await this.getWorkspaceRepo();
751+
const queryBuilder = workspaceRepo.createQueryBuilder("ws")
752+
.where("ws.type = :type", { type: type ? type.toString() : "regular" }); // only regular workspaces by default
740753

754+
return await queryBuilder.getCount();
755+
}
741756

742757
public async findAllWorkspaceAndInstances(offset: number, limit: number, orderBy: keyof WorkspaceAndInstance, orderDir: "ASC" | "DESC", query?: AdminGetWorkspacesQuery, searchTerm?: string): Promise<{ total: number, rows: WorkspaceAndInstance[] }> {
743758
let whereConditions = [];
@@ -827,7 +842,7 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB {
827842
const workspaceRepo = await this.getWorkspaceRepo();
828843
const workspace = await workspaceRepo.findOne(id);
829844
if (!workspace) {
830-
return;
845+
return;
831846
}
832847

833848
const instance = await this.findCurrentInstance(id);
@@ -899,7 +914,7 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB {
899914
});
900915
}
901916

902-
async findPrebuildInfos(prebuildIds: string[]): Promise<PrebuildInfo[]>{
917+
async findPrebuildInfos(prebuildIds: string[]): Promise<PrebuildInfo[]> {
903918
const repo = await this.getPrebuildInfoRepo();
904919

905920
const query = repo.createQueryBuilder('pi');
@@ -908,7 +923,7 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB {
908923
if (filteredIds.length === 0) {
909924
return [];
910925
}
911-
query.andWhere(`pi.prebuildId in (${ filteredIds.map(id => `'${id}'`).join(", ") })`)
926+
query.andWhere(`pi.prebuildId in (${filteredIds.map(id => `'${id}'`).join(", ")})`)
912927

913928
const res = await query.getMany();
914929
return res.map(r => r.info);

components/gitpod-db/src/workspace-db.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export interface WorkspacePortsAuthData {
3434
workspace: WorkspaceAuthData;
3535
}
3636

37-
export type WorkspaceInstanceSession = Pick<WorkspaceInstance, "id" | "startedTime"| "stoppingTime" | "stoppedTime">;
37+
export type WorkspaceInstanceSession = Pick<WorkspaceInstance, "id" | "startedTime" | "stoppingTime" | "stoppedTime">;
3838
export type WorkspaceSessionData = Pick<Workspace, "id" | "contextURL" | "context" | "type">;
3939
export interface WorkspaceInstanceSessionWithWorkspace {
4040
instance: WorkspaceInstanceSession;
@@ -67,7 +67,7 @@ export interface WorkspaceDB {
6767

6868
// Partial update: unconditional, single field updates. Enclose in a transaction if necessary
6969
updateLastHeartbeat(instanceId: string, userId: string, newHeartbeat: Date, wasClosed?: boolean): Promise<void>;
70-
getLastOwnerHeartbeatFor(instance: WorkspaceInstance): Promise<{ lastSeen:Date, wasClosed?: boolean} | undefined>;
70+
getLastOwnerHeartbeatFor(instance: WorkspaceInstance): Promise<{ lastSeen: Date, wasClosed?: boolean } | undefined>;
7171
getWorkspaceUsers(workspaceId: string, minLastSeen: number): Promise<WorkspaceInstanceUser[]>;
7272
updateInstancePartial(instanceId: string, partial: DeepPartial<WorkspaceInstance>): Promise<WorkspaceInstance>;
7373

@@ -85,12 +85,15 @@ export interface WorkspaceDB {
8585
findWorkspaceAndInstance(id: string): Promise<WorkspaceAndInstance | undefined>;
8686
findInstancesByPhaseAndRegion(phase: string, region: string): Promise<WorkspaceInstance[]>;
8787

88+
getWorkspaceCount(type?: String): Promise<Number>;
89+
getInstanceCount(type?: string): Promise<number>
90+
8891
findAllWorkspaceInstances(offset: number, limit: number, orderBy: keyof WorkspaceInstance, orderDir: "ASC" | "DESC", ownerId?: string, minCreationTime?: Date, maxCreationTime?: Date, onlyRunning?: boolean, type?: WorkspaceType): Promise<{ total: number, rows: WorkspaceInstance[] }>;
8992

9093
findRegularRunningInstances(userId?: string): Promise<WorkspaceInstance[]>;
9194
findRunningInstancesWithWorkspaces(installation?: string, userId?: string, includeStopping?: boolean): Promise<RunningWorkspaceInfo[]>;
9295

93-
isWhitelisted(repositoryUrl : string): Promise<boolean>;
96+
isWhitelisted(repositoryUrl: string): Promise<boolean>;
9497
getFeaturedRepositories(): Promise<Partial<WhitelistedRepository>[]>;
9598

9699
findSnapshotById(snapshotId: string): Promise<Snapshot | undefined>;

components/gitpod-protocol/src/installation-admin-protocol.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ export interface InstallationAdmin {
2323
settings: InstallationAdminSettings;
2424
}
2525

26+
export interface Data {
27+
installationAdmin: InstallationAdmin
28+
totalUsers: number
29+
totalWorkspaces: number
30+
totalInstances: number
31+
}
32+
2633
export namespace InstallationAdmin {
2734
export function createDefault(): InstallationAdmin {
2835
return {

components/installation-telemetry/cmd/send.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ var sendCmd = &cobra.Command{
3131
return err
3232
}
3333

34-
if !data.Settings.SendTelemetry {
34+
if !data.InstallationAdmin.Settings.SendTelemetry {
3535
log.Info("installation-telemetry is not permitted to send - exiting")
3636
return nil
3737
}
@@ -51,10 +51,13 @@ var sendCmd = &cobra.Command{
5151
}()
5252

5353
telemetry := analytics.Track{
54-
UserId: data.ID,
54+
UserId: data.InstallationAdmin.ID,
5555
Event: "Installation telemetry",
5656
Properties: analytics.NewProperties().
57-
Set("version", versionId),
57+
Set("version", versionId).
58+
Set("totalUsers", data.TotalUsers).
59+
Set("totalWorkspaces", data.TotalWorkspaces).
60+
Set("totalInstances", data.TotalInstances),
5861
}
5962

6063
client.Enqueue(telemetry)

components/installation-telemetry/pkg/server/installationAdmin.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,19 @@ type InstallationAdminSettings struct {
1717
SendTelemetry bool `json:"sendTelemetry"`
1818
}
1919

20-
type InstallationAdminData struct {
20+
type Data struct {
21+
InstallationAdmin InstallationAdmin `json:"installationAdmin"`
22+
TotalUsers int64 `json:"totalUsers"`
23+
TotalWorkspaces int64 `json:"totalWorkspaces"`
24+
TotalInstances int64 `json:"totalInstances"`
25+
}
26+
27+
type InstallationAdmin struct {
2128
ID string `json:"id"`
2229
Settings InstallationAdminSettings `json:"settings"`
2330
}
2431

25-
func GetInstallationAdminData(config common.Config) (*InstallationAdminData, error) {
32+
func GetInstallationAdminData(config common.Config) (*Data, error) {
2633
resp, err := http.Get(fmt.Sprintf("%s/data", config.Server))
2734
if err != nil {
2835
return nil, err
@@ -35,7 +42,7 @@ func GetInstallationAdminData(config common.Config) (*InstallationAdminData, err
3542
return nil, err
3643
}
3744

38-
var data InstallationAdminData
45+
var data Data
3946
if err := json.Unmarshal(body, &data); err != nil {
4047
return nil, err
4148
}

components/server/src/installation-admin/installation-admin-controller.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,25 @@
66

77
import { injectable, inject } from 'inversify';
88
import * as express from 'express';
9-
import { InstallationAdminDB } from '@gitpod/gitpod-db/lib';
9+
import { InstallationAdminDB, UserDB, WorkspaceDB } from '@gitpod/gitpod-db/lib';
10+
import { Data } from '@gitpod/gitpod-protocol'
1011

1112
@injectable()
1213
export class InstallationAdminController {
1314
@inject(InstallationAdminDB) protected readonly installationAdminDb: InstallationAdminDB;
15+
@inject(UserDB) protected readonly userDb: UserDB
16+
@inject(WorkspaceDB) protected readonly workspaceDb: WorkspaceDB
1417

1518
public create(): express.Application {
1619
const app = express();
1720

1821
app.get('/data', async (req: express.Request, res: express.Response) => {
19-
const data = await this.installationAdminDb.getData();
22+
const data: Data = {
23+
installationAdmin: await this.installationAdminDb.getData(),
24+
totalUsers: await this.userDb.getUserCount(true),
25+
totalWorkspaces: await this.workspaceDb.getWorkspaceCount(),
26+
totalInstances: await this.workspaceDb.getInstanceCount(),
27+
} as Data;
2028

2129
res.status(200).json(data);
2230
});

0 commit comments

Comments
 (0)