From d78575ed357ada97fa05bb73a7f687970fdb2fe9 Mon Sep 17 00:00:00 2001 From: Gero Posmyk-Leinemann Date: Thu, 6 Jun 2024 12:05:18 +0200 Subject: [PATCH] [server] Fix CapGitStatus job (#19855) --- .../server/src/jobs/cap-git-status.spec.db.ts | 93 +++++++++++++++++++ components/server/src/jobs/cap-git-status.ts | 7 +- 2 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 components/server/src/jobs/cap-git-status.spec.db.ts diff --git a/components/server/src/jobs/cap-git-status.spec.db.ts b/components/server/src/jobs/cap-git-status.spec.db.ts new file mode 100644 index 00000000000000..9217c60303d675 --- /dev/null +++ b/components/server/src/jobs/cap-git-status.spec.db.ts @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2024 Gitpod GmbH. All rights reserved. + * Licensed under the GNU Affero General Public License (AGPL). + * See License.AGPL.txt in the project root for license information. + */ + +import * as chai from "chai"; +const expect = chai.expect; +import { suite, test, timeout } from "@testdeck/mocha"; + +import { WorkspaceInstance, WorkspaceInstanceRepoStatus } from "@gitpod/gitpod-protocol"; +import { DBWorkspaceInstance, TypeORM, WorkspaceDB, resetDB } from "@gitpod/gitpod-db/lib"; +import { Repository } from "typeorm"; +import { CapGitStatus } from "./cap-git-status"; +import { createTestContainer } from "../test/service-testing-container-module"; +import { Container } from "inversify"; +import { Experiments } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server"; + +const workspaceId = "ws123"; + +@suite +class CapGitStatusTest { + container: Container; + db: WorkspaceDB; + + async before() { + this.container = createTestContainer(); + Experiments.configureTestingClient({ + api_validate_git_status_length: true, + }); + await this.wipeRepos(); + + this.db = this.container.get(WorkspaceDB); + } + + async after() { + // await this.wipeRepos(); + } + + async wipeRepos() { + const typeorm = this.container.get(TypeORM); + await resetDB(typeorm); + } + + instance(nr: number) { + const gitStatus: WorkspaceInstanceRepoStatus = { + branch: "main", + latestCommit: "abc", + untrackedFiles: [], + }; + for (let i = 0; i < 100; i++) { + gitStatus.untrackedFiles?.push(`file_${i}_${"a".repeat(100)}`); + } + gitStatus.totalUntrackedFiles = gitStatus.untrackedFiles?.length; + + const instance: WorkspaceInstance = { + id: `wsi${nr}`, + workspaceId, + creationTime: new Date(2018, 2, 16, 10, 0, 0).toISOString(), + ideUrl: "lalalal", + region: "somewhere", + workspaceImage: "eu.gcr.io/gitpod-io/workspace-images@sha256:123", + configuration: { + ideImage: "eu.gcr.io/gitpod-io/ide@sha256:123", + }, + status: { + phase: "stopped", + conditions: {}, + version: 1, + }, + gitStatus, + }; + return instance; + } + + @test(timeout(10000)) + public async createUserAndFindById() { + await this.db.storeInstance(this.instance(1)); + + const job = this.container.get(CapGitStatus); + expect(await findInstancesWithLengthyGitStatus(job, this.db), "instances with lengthy git status").to.equal(1); + expect(await job.run(), "number of capped instances").to.equal(1); + expect(await findInstancesWithLengthyGitStatus(job, this.db), "instances with lengthy git status").to.equal(0); + } +} + +async function findInstancesWithLengthyGitStatus(job: CapGitStatus, db: WorkspaceDB): Promise { + const repo = await ((db as any).getWorkspaceInstanceRepo() as Promise>); + const instances = await job.findInstancesWithLengthyGitStatus(repo, 4096, 100); + return instances.length; +} + +module.exports = new CapGitStatusTest(); diff --git a/components/server/src/jobs/cap-git-status.ts b/components/server/src/jobs/cap-git-status.ts index 84762bf8e08321..041832104bf20b 100644 --- a/components/server/src/jobs/cap-git-status.ts +++ b/components/server/src/jobs/cap-git-status.ts @@ -44,10 +44,9 @@ export class CapGitStatus implements Job { } // Cap the git status (incl. status.repo, the old place where we stored it before) - const MARGIN = 200; instances.forEach((i) => { if (i.gitStatus) { - i.gitStatus = capGitStatus(i.gitStatus, GIT_STATUS_LENGTH_CAP_BYTES - MARGIN); + i.gitStatus = capGitStatus(i.gitStatus); } if (i.status) { delete (i.status as any).repo; @@ -80,7 +79,9 @@ export class CapGitStatus implements Job { } } -function capGitStatus(gitStatus: WorkspaceInstanceRepoStatus, maxLength: number): WorkspaceInstanceRepoStatus { +function capGitStatus(gitStatus: WorkspaceInstanceRepoStatus): WorkspaceInstanceRepoStatus { + const MARGIN = 500; // to account for attribute name's, and generic JSON overhead + const maxLength = GIT_STATUS_LENGTH_CAP_BYTES - MARGIN; let bytesUsed = 0; function capStr(str: string | undefined): string | undefined { if (str === undefined) {