Path: blob/main/components/ws-manager-bridge/src/app-cluster-instance-controller.ts
2498 views
/**1* Copyright (c) 2022 Gitpod GmbH. All rights reserved.2* Licensed under the GNU Affero General Public License (AGPL).3* See License.AGPL.txt in the project root for license information.4*/56import { WorkspaceDB } from "@gitpod/gitpod-db/lib/workspace-db";7import { Disposable, DisposableCollection } from "@gitpod/gitpod-protocol";8import { log } from "@gitpod/gitpod-protocol/lib/util/logging";9import { repeat } from "@gitpod/gitpod-protocol/lib/util/repeat";10import { TraceContext } from "@gitpod/gitpod-protocol/lib/util/tracing";11import { inject, injectable } from "inversify";12import { Configuration } from "./config";13import { WorkspaceInstanceController } from "./workspace-instance-controller";1415/**16* The WorkspaceInstance lifecycle is split between application clusters and workspace clusters on the transition from17* pending/building -> starting (cmp. WorkspacePhases here:18* https://github.com/gitpod-io/gitpod/blob/008ea3fadc89d4817cf3effc8a5b30eaf469fb1c/components/gitpod-protocol/src/workspace-instance.ts#L111).19*20* Before the transition, WorkspaceInstances belong to the respective app cluster, denoted by "instance.region === 'eu02'", for exmaple.21* After a WorkspaceInstance has been moved over to a workspace cluster, that moved "ownership" is reflected in said field.22* We maintain a constant connection (called "bridge") to all workspace clusters to be able to keep reality (workspace23* side) in sync with what we have in our DB/forward to clients.24*25* This class is meant to take the same responsibility for all WorkspaceInstances that have not (yet) been passed over26* to a workspace cluster for whatever reason. Here's a list of examples, prefixed by phase:27* - "preparing": failed cleanup after failed call to wsManager.StartWorkspace28* - "building": failed cleanup after failed image-build (which is still controlled by the application cluster,29* although that might change in the future)30*/31@injectable()32export class AppClusterWorkspaceInstancesController implements Disposable {33constructor(34@inject(Configuration) private readonly config: Configuration,35@inject(WorkspaceDB) private readonly workspaceDb: WorkspaceDB,36@inject(WorkspaceInstanceController)37private readonly workspaceInstanceController: WorkspaceInstanceController,38) {}3940private readonly dispoables = new DisposableCollection();4142public async start() {43const disposable = repeat(44async () => this.controlAppClusterManagedWorkspaceInstances(),45this.config.controllerIntervalSeconds * 1000,46);47this.dispoables.push(disposable);48}4950private async controlAppClusterManagedWorkspaceInstances() {51const appClusterInstallation = this.config.installation;5253const span = TraceContext.startSpan("controlAppClusterManagedWorkspaceInstances");54const ctx = { span };55try {56log.info("Controlling app cluster instances", { installation: appClusterInstallation });5758const notStoppedInstances = await this.workspaceDb.findRunningInstancesWithWorkspaces(59appClusterInstallation,60undefined,61false,62);63await this.workspaceInstanceController.controlNotStoppedAppClusterManagedInstanceTimeouts(64ctx,65notStoppedInstances,66appClusterInstallation,67);6869log.info("Done controlling app cluster instances", {70installation: appClusterInstallation,71instancesCount: notStoppedInstances.length,72});73} catch (err) {74log.error("Error controlling app cluster instances", err, {75installation: appClusterInstallation,76});77TraceContext.setError(ctx, err);78} finally {79span.finish();80}81}8283public dispose() {84this.dispoables.dispose();85}86}878889