diff --git a/components/gitpod-db/src/typeorm/workspace-db-impl.ts b/components/gitpod-db/src/typeorm/workspace-db-impl.ts index ed06d4eb7a206b..32e343782ff363 100644 --- a/components/gitpod-db/src/typeorm/workspace-db-impl.ts +++ b/components/gitpod-db/src/typeorm/workspace-db-impl.ts @@ -52,6 +52,7 @@ import { DBPrebuiltWorkspace } from "./entity/db-prebuilt-workspace"; import { DBPrebuiltWorkspaceUpdatable } from "./entity/db-prebuilt-workspace-updatable"; import { BUILTIN_WORKSPACE_PROBE_USER_ID } from "../user-db"; import { DBPrebuildInfo } from "./entity/db-prebuild-info-entry"; +import { daysBefore } from "@gitpod/gitpod-protocol/lib/util/timeutil"; type RawTo = (instance: WorkspaceInstance, ws: Workspace) => T; interface OrderBy { @@ -592,6 +593,22 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { return dbResults as WorkspaceAndOwner[]; } + public async findWorkspacesForPurging( + minContentDeletionTimeInDays: number, + limit: number, + now: Date, + ): Promise { + const minPurgeTime = daysBefore(now.toISOString(), minContentDeletionTimeInDays); + const repo = await this.getWorkspaceRepo(); + const qb = repo + .createQueryBuilder("ws") + .select(["ws.id", "ws.ownerId"]) + .where(`ws.contentDeletedTime != ''`) + .andWhere(`ws.contentDeletedTime < :minPurgeTime`, { minPurgeTime }) + .limit(limit); + return await qb.getMany(); + } + public async findWorkspacesForContentDeletion( minSoftDeletedTimeInDays: number, limit: number, @@ -964,8 +981,8 @@ export abstract class AbstractTypeORMWorkspaceDBImpl implements WorkspaceDB { * around to deleting them. */ public async hardDeleteWorkspace(workspaceId: string): Promise { - await (await this.getWorkspaceRepo()).update(workspaceId, { deleted: true }); await (await this.getWorkspaceInstanceRepo()).update({ workspaceId }, { deleted: true }); + await (await this.getWorkspaceRepo()).update(workspaceId, { deleted: true }); } public async findAllWorkspaces( diff --git a/components/gitpod-db/src/workspace-db.spec.db.ts b/components/gitpod-db/src/workspace-db.spec.db.ts index 9d4b38b9b1319d..ae24c1a83e4d34 100644 --- a/components/gitpod-db/src/workspace-db.spec.db.ts +++ b/components/gitpod-db/src/workspace-db.spec.db.ts @@ -662,6 +662,7 @@ class WorkspaceDBSpec { ]); } } + @test(timeout(10000)) public async testFindVolumeSnapshotWorkspacesForGC() { await this.threeVolumeSnapshotsForTwoWorkspaces(); @@ -726,5 +727,77 @@ class WorkspaceDBSpec { workspaceId: workspaceId2, }); } + + @test(timeout(10000)) + public async findWorkspacesForPurging() { + const creationTime = "2018-01-01T00:00:00.000Z"; + const ownerId = "1221423"; + const purgeDate = new Date("2019-02-01T00:00:00.000Z"); + const d20180202 = "2018-02-02T00:00:00.000Z"; + const d20180201 = "2018-02-01T00:00:00.000Z"; + const d20180131 = "2018-01-31T00:00:00.000Z"; + await Promise.all([ + this.db.store({ + id: "1", + creationTime, + description: "something", + contextURL: "http://github.com/myorg/inactive", + ownerId, + context: { + title: "my title", + }, + config: {}, + type: "regular", + contentDeletedTime: d20180131, + }), + this.db.store({ + id: "2", + creationTime, + description: "something", + contextURL: "http://github.com/myorg/active", + ownerId, + context: { + title: "my title", + }, + config: {}, + type: "regular", + contentDeletedTime: d20180201, + }), + this.db.store({ + id: "3", + creationTime, + description: "something", + contextURL: "http://github.com/myorg/active", + ownerId, + context: { + title: "my title", + }, + config: {}, + type: "regular", + contentDeletedTime: d20180202, + }), + this.db.store({ + id: "4", + creationTime, + description: "something", + contextURL: "http://github.com/myorg/active", + ownerId, + context: { + title: "my title", + }, + config: {}, + type: "regular", + contentDeletedTime: undefined, + }), + ]); + + const wsIds = await this.db.findWorkspacesForPurging(365, 1000, purgeDate); + expect(wsIds).to.deep.equal([ + { + id: "1", + ownerId, + }, + ]); + } } module.exports = new WorkspaceDBSpec(); diff --git a/components/gitpod-db/src/workspace-db.ts b/components/gitpod-db/src/workspace-db.ts index 6c33660a780252..35881410412282 100644 --- a/components/gitpod-db/src/workspace-db.ts +++ b/components/gitpod-db/src/workspace-db.ts @@ -108,6 +108,11 @@ export interface WorkspaceDB { minSoftDeletedTimeInDays: number, limit: number, ): Promise; + findWorkspacesForPurging( + minContentDeletionTimeInDays: number, + limit: number, + now: Date, + ): Promise; findPrebuiltWorkspacesForGC(daysUnused: number, limit: number): Promise; findAllWorkspaces( offset: number, diff --git a/components/gitpod-protocol/src/util/timeutil.spec.ts b/components/gitpod-protocol/src/util/timeutil.spec.ts index e518856912e248..13762156ae5dae 100644 --- a/components/gitpod-protocol/src/util/timeutil.spec.ts +++ b/components/gitpod-protocol/src/util/timeutil.spec.ts @@ -45,4 +45,25 @@ export class TimeutilSpec { const later = oneMonthLater(from.toISOString(), day); expect(later, `expected ${later} to be equal ${expectation}`).to.be.equal(expectation.toISOString()); } + + @test + testDaysBefore2() { + const tests: { date: Date; daysEarlier: number; expectation: string }[] = [ + { + date: new Date("2021-07-13T00:00:00.000Z"), + daysEarlier: 365, + expectation: "2020-07-13T00:00:00.000Z", + }, + { + date: new Date("2019-02-01T00:00:00.000Z"), + daysEarlier: 365, + expectation: "2018-02-01T00:00:00.000Z", + }, + ]; + + for (const t of tests) { + const actual = daysBefore(t.date.toISOString(), t.daysEarlier); + expect(actual).to.equal(t.expectation, `expected ${actual} to be equal ${t.expectation}`); + } + } } diff --git a/components/server/src/config.ts b/components/server/src/config.ts index 2d92148653c3de..2090895dd55483 100644 --- a/components/server/src/config.ts +++ b/components/server/src/config.ts @@ -43,11 +43,30 @@ export interface WorkspaceDefaults { export interface WorkspaceGarbageCollection { disabled: boolean; startDate: number; + + /** The number of seconds between a run and the next */ + intervalSeconds: number; + + /** The maximum amount of workspaces that are marked as 'softDeleted' in one go */ chunkLimit: number; + + /** The minimal age of a workspace before it is marked as 'softDeleted' (= hidden for the user) */ minAgeDays: number; + + /** The minimal age of a prebuild (incl. workspace) before it's content is deleted (+ marked as 'softDeleted') */ minAgePrebuildDays: number; + + /** The minimal number of days a workspace has to stay in 'softDeleted' before it's content is deleted */ contentRetentionPeriodDays: number; + + /** The maximum amount of workspaces whose content is deleted in one go */ contentChunkLimit: number; + + /** The minimal number of days a workspace has to stay in 'contentDeleted' before it's purged from the DB */ + purgeRetentionPeriodDays: number; + + /** The maximum amount of workspaces which are purged in one go */ + purgeChunkLimit: number; } /** diff --git a/components/server/src/prometheus-metrics.ts b/components/server/src/prometheus-metrics.ts index 206d9787ad96e3..84bda220637b95 100644 --- a/components/server/src/prometheus-metrics.ts +++ b/components/server/src/prometheus-metrics.ts @@ -166,3 +166,13 @@ const prebuildsStartedTotal = new prometheusClient.Counter({ export function increasePrebuildsStartedCounter() { prebuildsStartedTotal.inc(); } + +const workspacesPurgedTotal = new prometheusClient.Counter({ + name: "gitpod_server_workspaces_purged_total", + help: "Counter of workspaces hard deleted by periodic job running on server.", + registers: [prometheusClient.register], +}); + +export function reportWorkspacePurged() { + workspacesPurgedTotal.inc(); +} diff --git a/components/server/src/workspace/garbage-collector.ts b/components/server/src/workspace/garbage-collector.ts index 2f8b5882a5a317..c1f1bd213d99c5 100644 --- a/components/server/src/workspace/garbage-collector.ts +++ b/components/server/src/workspace/garbage-collector.ts @@ -34,7 +34,10 @@ export class WorkspaceGarbageCollector { dispose: () => {}, }; } - return repeat(async () => this.garbageCollectWorkspacesIfLeader(), 30 * 60 * 1000); + return repeat( + async () => this.garbageCollectWorkspacesIfLeader(), + this.config.workspaceGarbageCollection.intervalSeconds * 1000, + ); } public async garbageCollectWorkspacesIfLeader() { @@ -44,6 +47,9 @@ export class WorkspaceGarbageCollector { this.deleteWorkspaceContentAfterRetentionPeriod().catch((err) => log.error("wsgc: error during content deletion", err), ); + this.purgeWorkspacesAfterPurgeRetentionPeriod().catch((err) => + log.error("wsgc: error during hard deletion of workspaces", err), + ); this.deleteOldPrebuilds().catch((err) => log.error("wsgc: error during prebuild deletion", err)); this.deleteOutdatedVolumeSnapshots().catch((err) => log.error("wsgc: error during volume snapshot gc deletion", err), @@ -105,6 +111,34 @@ export class WorkspaceGarbageCollector { } } + /** + * This method is meant to purge all traces of a Workspace and it's WorkspaceInstances from the DB + */ + protected async purgeWorkspacesAfterPurgeRetentionPeriod() { + const span = opentracing.globalTracer().startSpan("purgeWorkspacesAfterPurgeRetentionPeriod"); + try { + const now = new Date(); + const workspaces = await this.workspaceDB + .trace({ span }) + .findWorkspacesForPurging( + this.config.workspaceGarbageCollection.purgeRetentionPeriodDays, + this.config.workspaceGarbageCollection.purgeChunkLimit, + now, + ); + const deletes = await Promise.all( + workspaces.map((ws) => this.deletionService.hardDeleteWorkspace({ span }, ws.id)), + ); + + log.info(`wsgc: successfully purged ${deletes.length} workspaces`); + span.addTags({ nrOfCollectedWorkspaces: deletes.length }); + } catch (err) { + TraceContext.setError({ span }, err); + throw err; + } finally { + span.finish(); + } + } + protected async deleteOldPrebuilds() { const span = opentracing.globalTracer().startSpan("deleteOldPrebuilds"); try { @@ -128,7 +162,7 @@ export class WorkspaceGarbageCollector { } } - // finds volume snapshots that have been superceded by newer volume snapshot and removes them + // finds volume snapshots that have been superseded by newer volume snapshot and removes them protected async deleteOutdatedVolumeSnapshots() { const span = opentracing.globalTracer().startSpan("deleteOutdatedVolumeSnapshots"); try { diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts index 9f17326403a967..5d8fac1b09f2cb 100644 --- a/components/server/src/workspace/gitpod-server-impl.ts +++ b/components/server/src/workspace/gitpod-server-impl.ts @@ -1166,7 +1166,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable { try { await this.guardAccess({ kind: "workspace", subject: workspace }, "create"); } catch (err) { - await this.workspaceDb.trace(ctx).hardDeleteWorkspace(workspace.id); + await this.workspaceDeletionService.hardDeleteWorkspace(ctx, workspace.id); throw err; } diff --git a/components/server/src/workspace/workspace-deletion-service.ts b/components/server/src/workspace/workspace-deletion-service.ts index df5ea59d935048..68306e07a67f70 100644 --- a/components/server/src/workspace/workspace-deletion-service.ts +++ b/components/server/src/workspace/workspace-deletion-service.ts @@ -19,6 +19,7 @@ import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing"; import { WorkspaceManagerClientProvider } from "@gitpod/ws-manager/lib/client-provider"; import { DeleteVolumeSnapshotRequest } from "@gitpod/ws-manager/lib"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; +import { reportWorkspacePurged } from "../prometheus-metrics"; @injectable() export class WorkspaceDeletionService { @@ -46,6 +47,19 @@ export class WorkspaceDeletionService { }); } + /** + * This *hard deletes* the workspace entry and all corresponding workspace-instances, by triggering a db-sync mechanism that purges it from the DB. + * Note: when this function returns that doesn't mean that the entries are actually gone yet, that might still take a short while until db-sync comes + * around to deleting them. + * @param ctx + * @param workspaceId + */ + public async hardDeleteWorkspace(ctx: TraceContext, workspaceId: string): Promise { + await this.db.trace(ctx).hardDeleteWorkspace(workspaceId); + log.info(`Purged Workspace ${workspaceId} and all WorkspaceInstances for this workspace`, { workspaceId }); + reportWorkspacePurged(); + } + /** * This method garbageCollects a workspace. It deletes its contents and sets the workspaces 'contentDeletedTime' * @param ctx diff --git a/install/installer/cmd/testdata/render/aws-setup/output.golden b/install/installer/cmd/testdata/render/aws-setup/output.golden index 0dc5ef55faa1df..c2119a76d4683d 100644 --- a/install/installer/cmd/testdata/render/aws-setup/output.golden +++ b/install/installer/cmd/testdata/render/aws-setup/output.golden @@ -4234,11 +4234,14 @@ data: "workspaceGarbageCollection": { "disabled": false, "startDate": 0, + "intervalSeconds": 1800, "chunkLimit": 1000, "minAgeDays": 14, "minAgePrebuildDays": 7, "contentRetentionPeriodDays": 21, - "contentChunkLimit": 1000 + "contentChunkLimit": 1000, + "purgeRetentionPeriodDays": 1095, + "purgeChunkLimit": 1000 }, "authProviderConfigFiles": [], "incrementalPrebuilds": { @@ -8374,7 +8377,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 32e8b437cc6763cf35540014d9f2d93632243e2da7bfac04edb928dbd31193e0 + gitpod.io/checksum_config: 49fb17d3a5da86da4235edbf6ed37500f9b781371f6d605ee504e295f23d0aae creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/azure-setup/output.golden b/install/installer/cmd/testdata/render/azure-setup/output.golden index 0cf5cbfab87756..18f01f1d792b7f 100644 --- a/install/installer/cmd/testdata/render/azure-setup/output.golden +++ b/install/installer/cmd/testdata/render/azure-setup/output.golden @@ -4098,11 +4098,14 @@ data: "workspaceGarbageCollection": { "disabled": false, "startDate": 0, + "intervalSeconds": 1800, "chunkLimit": 1000, "minAgeDays": 14, "minAgePrebuildDays": 7, "contentRetentionPeriodDays": 21, - "contentChunkLimit": 1000 + "contentChunkLimit": 1000, + "purgeRetentionPeriodDays": 1095, + "purgeChunkLimit": 1000 }, "authProviderConfigFiles": [], "incrementalPrebuilds": { @@ -8226,7 +8229,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 32e8b437cc6763cf35540014d9f2d93632243e2da7bfac04edb928dbd31193e0 + gitpod.io/checksum_config: 49fb17d3a5da86da4235edbf6ed37500f9b781371f6d605ee504e295f23d0aae creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/customization/output.golden b/install/installer/cmd/testdata/render/customization/output.golden index dfd3adbaf20db5..58f3bd7796e468 100644 --- a/install/installer/cmd/testdata/render/customization/output.golden +++ b/install/installer/cmd/testdata/render/customization/output.golden @@ -5035,11 +5035,14 @@ data: "workspaceGarbageCollection": { "disabled": false, "startDate": 0, + "intervalSeconds": 1800, "chunkLimit": 1000, "minAgeDays": 14, "minAgePrebuildDays": 7, "contentRetentionPeriodDays": 21, - "contentChunkLimit": 1000 + "contentChunkLimit": 1000, + "purgeRetentionPeriodDays": 1095, + "purgeChunkLimit": 1000 }, "authProviderConfigFiles": [], "incrementalPrebuilds": { @@ -9784,7 +9787,7 @@ spec: metadata: annotations: gitpod.io: hello - gitpod.io/checksum_config: 7f31019ddffd47a37a2fa1c2688b77bbf03a774a8af3c7a289017e74a4c84a20 + gitpod.io/checksum_config: aab520446d4937685907939b2aea9111f2a691427cb4bdbf1b586f1b15b10f08 hello: world creationTimestamp: null labels: diff --git a/install/installer/cmd/testdata/render/external-registry/output.golden b/install/installer/cmd/testdata/render/external-registry/output.golden index a87d443caca5e3..471bfe353d77bf 100644 --- a/install/installer/cmd/testdata/render/external-registry/output.golden +++ b/install/installer/cmd/testdata/render/external-registry/output.golden @@ -4285,11 +4285,14 @@ data: "workspaceGarbageCollection": { "disabled": false, "startDate": 0, + "intervalSeconds": 1800, "chunkLimit": 1000, "minAgeDays": 14, "minAgePrebuildDays": 7, "contentRetentionPeriodDays": 21, - "contentChunkLimit": 1000 + "contentChunkLimit": 1000, + "purgeRetentionPeriodDays": 1095, + "purgeChunkLimit": 1000 }, "authProviderConfigFiles": [], "incrementalPrebuilds": { @@ -8652,7 +8655,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: b2674e6368640d5a4b4ccb0f2fa8cb4e521fb3ee60c58f38d8b57272063a8bea + gitpod.io/checksum_config: bc10d1cc9e580a47b65956152d9caad8a870aba5ea805523e17282cb8533b8e8 creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/gcp-setup/output.golden b/install/installer/cmd/testdata/render/gcp-setup/output.golden index b9e841d9a13576..8d15802a30fd4e 100644 --- a/install/installer/cmd/testdata/render/gcp-setup/output.golden +++ b/install/installer/cmd/testdata/render/gcp-setup/output.golden @@ -4059,11 +4059,14 @@ data: "workspaceGarbageCollection": { "disabled": false, "startDate": 0, + "intervalSeconds": 1800, "chunkLimit": 1000, "minAgeDays": 14, "minAgePrebuildDays": 7, "contentRetentionPeriodDays": 21, - "contentChunkLimit": 1000 + "contentChunkLimit": 1000, + "purgeRetentionPeriodDays": 1095, + "purgeChunkLimit": 1000 }, "authProviderConfigFiles": [], "incrementalPrebuilds": { @@ -8155,7 +8158,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 4f66681a8061c909af839092f3667c674ae68889a700f1af87162eafb05f72ac + gitpod.io/checksum_config: be459a9d71866b87da87cd017d0063334112640ca956927773eb7adaf885c00b creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/http-proxy/output.golden b/install/installer/cmd/testdata/render/http-proxy/output.golden index 4bda0b05712cd4..38e9c30131bae4 100644 --- a/install/installer/cmd/testdata/render/http-proxy/output.golden +++ b/install/installer/cmd/testdata/render/http-proxy/output.golden @@ -4508,11 +4508,14 @@ data: "workspaceGarbageCollection": { "disabled": false, "startDate": 0, + "intervalSeconds": 1800, "chunkLimit": 1000, "minAgeDays": 14, "minAgePrebuildDays": 7, "contentRetentionPeriodDays": 21, - "contentChunkLimit": 1000 + "contentChunkLimit": 1000, + "purgeRetentionPeriodDays": 1095, + "purgeChunkLimit": 1000 }, "authProviderConfigFiles": [], "incrementalPrebuilds": { @@ -10073,7 +10076,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: b2674e6368640d5a4b4ccb0f2fa8cb4e521fb3ee60c58f38d8b57272063a8bea + gitpod.io/checksum_config: bc10d1cc9e580a47b65956152d9caad8a870aba5ea805523e17282cb8533b8e8 creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/minimal/output.golden b/install/installer/cmd/testdata/render/minimal/output.golden index 9d01b538fd031d..0750e78a25a86f 100644 --- a/install/installer/cmd/testdata/render/minimal/output.golden +++ b/install/installer/cmd/testdata/render/minimal/output.golden @@ -4505,11 +4505,14 @@ data: "workspaceGarbageCollection": { "disabled": false, "startDate": 0, + "intervalSeconds": 1800, "chunkLimit": 1000, "minAgeDays": 14, "minAgePrebuildDays": 7, "contentRetentionPeriodDays": 21, - "contentChunkLimit": 1000 + "contentChunkLimit": 1000, + "purgeRetentionPeriodDays": 1095, + "purgeChunkLimit": 1000 }, "authProviderConfigFiles": [], "incrementalPrebuilds": { @@ -9027,7 +9030,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: b2674e6368640d5a4b4ccb0f2fa8cb4e521fb3ee60c58f38d8b57272063a8bea + gitpod.io/checksum_config: bc10d1cc9e580a47b65956152d9caad8a870aba5ea805523e17282cb8533b8e8 creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/statefulset-customization/output.golden b/install/installer/cmd/testdata/render/statefulset-customization/output.golden index 3cc0ab8aafc4ae..2ca67c2671e1f5 100644 --- a/install/installer/cmd/testdata/render/statefulset-customization/output.golden +++ b/install/installer/cmd/testdata/render/statefulset-customization/output.golden @@ -4517,11 +4517,14 @@ data: "workspaceGarbageCollection": { "disabled": false, "startDate": 0, + "intervalSeconds": 1800, "chunkLimit": 1000, "minAgeDays": 14, "minAgePrebuildDays": 7, "contentRetentionPeriodDays": 21, - "contentChunkLimit": 1000 + "contentChunkLimit": 1000, + "purgeRetentionPeriodDays": 1095, + "purgeChunkLimit": 1000 }, "authProviderConfigFiles": [], "incrementalPrebuilds": { @@ -9039,7 +9042,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: b2674e6368640d5a4b4ccb0f2fa8cb4e521fb3ee60c58f38d8b57272063a8bea + gitpod.io/checksum_config: bc10d1cc9e580a47b65956152d9caad8a870aba5ea805523e17282cb8533b8e8 creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/use-pod-security-policies/output.golden b/install/installer/cmd/testdata/render/use-pod-security-policies/output.golden index fb946f3b022bda..9700dd11885c7e 100644 --- a/install/installer/cmd/testdata/render/use-pod-security-policies/output.golden +++ b/install/installer/cmd/testdata/render/use-pod-security-policies/output.golden @@ -4838,11 +4838,14 @@ data: "workspaceGarbageCollection": { "disabled": false, "startDate": 0, + "intervalSeconds": 1800, "chunkLimit": 1000, "minAgeDays": 14, "minAgePrebuildDays": 7, "contentRetentionPeriodDays": 21, - "contentChunkLimit": 1000 + "contentChunkLimit": 1000, + "purgeRetentionPeriodDays": 1095, + "purgeChunkLimit": 1000 }, "authProviderConfigFiles": [], "incrementalPrebuilds": { @@ -9471,7 +9474,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: b2674e6368640d5a4b4ccb0f2fa8cb4e521fb3ee60c58f38d8b57272063a8bea + gitpod.io/checksum_config: bc10d1cc9e580a47b65956152d9caad8a870aba5ea805523e17282cb8533b8e8 creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/workspace-requests-limits/output.golden b/install/installer/cmd/testdata/render/workspace-requests-limits/output.golden index d5d70e73cf5049..b9536c7f34b1aa 100644 --- a/install/installer/cmd/testdata/render/workspace-requests-limits/output.golden +++ b/install/installer/cmd/testdata/render/workspace-requests-limits/output.golden @@ -4508,11 +4508,14 @@ data: "workspaceGarbageCollection": { "disabled": false, "startDate": 0, + "intervalSeconds": 1800, "chunkLimit": 1000, "minAgeDays": 14, "minAgePrebuildDays": 7, "contentRetentionPeriodDays": 21, - "contentChunkLimit": 1000 + "contentChunkLimit": 1000, + "purgeRetentionPeriodDays": 1095, + "purgeChunkLimit": 1000 }, "authProviderConfigFiles": [], "incrementalPrebuilds": { @@ -9030,7 +9033,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: b2674e6368640d5a4b4ccb0f2fa8cb4e521fb3ee60c58f38d8b57272063a8bea + gitpod.io/checksum_config: bc10d1cc9e580a47b65956152d9caad8a870aba5ea805523e17282cb8533b8e8 creationTimestamp: null labels: app: gitpod diff --git a/install/installer/pkg/components/server/configmap.go b/install/installer/pkg/components/server/configmap.go index d66f379b1c0638..e3fb1c999e6040 100644 --- a/install/installer/pkg/components/server/configmap.go +++ b/install/installer/pkg/components/server/configmap.go @@ -186,12 +186,15 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { DefinitelyGpDisabled: ctx.Config.DisableDefinitelyGP, GitHubApp: githubApp, WorkspaceGarbageCollection: WorkspaceGarbageCollection{ - ChunkLimit: 1000, - ContentChunkLimit: 1000, - ContentRetentionPeriodDays: 21, Disabled: disableWsGarbageCollection, + IntervalSeconds: 1800, MinAgeDays: 14, MinAgePrebuildDays: 7, + ChunkLimit: 1000, + ContentRetentionPeriodDays: 21, + ContentChunkLimit: 1000, + PurgeRetentionPeriodDays: 365 * 3, + PurgeChunkLimit: 1000, }, EnableLocalApp: enableLocalApp, AuthProviderConfigFiles: func() []string { diff --git a/install/installer/pkg/components/server/types.go b/install/installer/pkg/components/server/types.go index f561b4c14fdc13..71908c324ab7fe 100644 --- a/install/installer/pkg/components/server/types.go +++ b/install/installer/pkg/components/server/types.go @@ -91,11 +91,14 @@ type IncrementalPrebuilds struct { type WorkspaceGarbageCollection struct { Disabled bool `json:"disabled"` StartDate int32 `json:"startDate"` + IntervalSeconds int32 `json:"intervalSeconds"` ChunkLimit int32 `json:"chunkLimit"` MinAgeDays int32 `json:"minAgeDays"` MinAgePrebuildDays int32 `json:"minAgePrebuildDays"` ContentRetentionPeriodDays int32 `json:"contentRetentionPeriodDays"` ContentChunkLimit int32 `json:"contentChunkLimit"` + PurgeRetentionPeriodDays int32 `json:"purgeRetentionPeriodDays"` + PurgeChunkLimit int32 `json:"purgeChunkLimit"` } type GitHubApp struct {