/** * Copyright (c) 2020 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 { DeepPartial } from 'typeorm'; import { injectable } from 'inversify'; import { Workspace, WorkspaceInfo, WorkspaceInstance, WorkspaceInstanceUser, WhitelistedRepository, Snapshot, LayoutData, PrebuiltWorkspace, PrebuiltWorkspaceUpdatable, RunningWorkspaceInfo, WorkspaceAndInstance, WorkspaceType } from '@gitpod/gitpod-protocol'; export type MaybeWorkspace = Workspace | undefined; export type MaybeWorkspaceInstance = WorkspaceInstance | undefined; export interface FindWorkspacesOptions { userId: string limit?: number searchString?: string includeHeadless?: boolean pinnedOnly?: boolean } export interface PrebuiltUpdatableAndWorkspace extends PrebuiltWorkspaceUpdatable { prebuild: PrebuiltWorkspace workspace: Workspace instance: WorkspaceInstance } export type WorkspaceAuthData = Pick; export type WorkspaceInstancePortsAuthData = Pick; export interface WorkspacePortsAuthData { instance: WorkspaceInstancePortsAuthData; workspace: WorkspaceAuthData; } export type WorkspaceInstanceSession = Pick; export type WorkspaceSessionData = Pick; export interface WorkspaceInstanceSessionWithWorkspace { instance: WorkspaceInstanceSession; workspace: WorkspaceSessionData; } export interface PrebuildWithWorkspace { prebuild: PrebuiltWorkspace; workspace: Workspace; } export type WorkspaceAndOwner = Pick; export type WorkspaceOwnerAndSoftDeleted = Pick; export const WorkspaceDB = Symbol('WorkspaceDB'); export interface WorkspaceDB { connect(maxTries: number, timeout: number): Promise; transaction(code: (db: WorkspaceDB) => Promise): Promise; store(workspace: Workspace): Promise; updatePartial(workspaceId: string, partial: DeepPartial): Promise; findById(id: string): Promise; findByInstanceId(id: string): Promise; find(options: FindWorkspacesOptions): Promise; findWorkspacePortsAuthDataById(workspaceId: string): Promise; storeInstance(instance: WorkspaceInstance): Promise; // Partial update: unconditional, single field updates. Enclose in a transaction if necessary updateLastHeartbeat(instanceId: string, userId: string, newHeartbeat: Date, wasClosed?: boolean): Promise; getLastOwnerHeartbeatFor(instance: WorkspaceInstance): Promise<{ lastSeen:Date, wasClosed?: boolean} | undefined>; getWorkspaceUsers(workspaceId: string, minLastSeen: number): Promise; updateInstancePartial(instanceId: string, partial: DeepPartial): Promise; findInstanceById(workspaceInstanceId: string): Promise; findInstances(workspaceId: string): Promise; findWorkspacesByUser(userId: string): Promise; findCurrentInstance(workspaceId: string): Promise; findRunningInstance(workspaceId: string): Promise; findSessionsInPeriod(userId: string, periodStart: string, periodEnd: string): Promise; findWorkspacesForGarbageCollection(minAgeInDays: number, limit: number): Promise; findWorkspacesForContentDeletion(minSoftDeletedTimeInDays: number, limit: number): Promise; findPrebuiltWorkspacesForGC(daysUnused: number, limit: number): Promise; findAllWorkspaces(offset: number, limit: number, orderBy: keyof Workspace, orderDir: "ASC" | "DESC", ownerId?: string, searchTerm?: string, minCreationTime?: Date, maxCreationDateTime?: Date, type?: WorkspaceType): Promise<{ total: number, rows: Workspace[] }>; findAllWorkspaceAndInstances(offset: number, limit: number, orderBy: keyof WorkspaceAndInstance, orderDir: "ASC" | "DESC", ownerId?: string, searchTerm?: string): Promise<{ total: number, rows: WorkspaceAndInstance[] }>; findWorkspaceAndInstance(id: string): Promise; 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[] }>; findRegularRunningInstances(userId?: string): Promise; findRunningInstancesWithWorkspaces(installation?: string, userId?: string): Promise; isWhitelisted(repositoryUrl : string): Promise; getFeaturedRepositories(): Promise[]>; findSnapshotById(snapshotId: string): Promise; findSnapshotsByWorkspaceId(workspaceId: string): Promise; storeSnapshot(snapshot: Snapshot): Promise; getTotalPrebuildUseSeconds(forDays: number): Promise; storePrebuiltWorkspace(pws: PrebuiltWorkspace): Promise; findPrebuiltWorkspaceByCommit(cloneURL: string, commit: string): Promise; findPrebuildsWithWorkpace(cloneURL: string): Promise; findPrebuildByWorkspaceID(wsid: string): Promise; findPrebuildByID(pwsid: string): Promise; countRunningPrebuilds(cloneURL: string): Promise; findQueuedPrebuilds(cloneURL?: string): Promise; attachUpdatableToPrebuild(pwsid: string, update: PrebuiltWorkspaceUpdatable): Promise; findUpdatablesForPrebuild(pwsid: string): Promise; markUpdatableResolved(updatableId: string): Promise; getUnresolvedUpdatables(): Promise; findLayoutDataByWorkspaceId(workspaceId: string): Promise; storeLayoutData(layoutData: LayoutData): Promise; hardDeleteWorkspace(workspaceID: string): Promise; } @injectable() export abstract class AbstractWorkspaceDB implements WorkspaceDB { abstract connect(maxTries: number, timeout: number): Promise; abstract store(workspace: Workspace): Promise; abstract updatePartial(workspaceId: string, partial: DeepPartial): Promise; abstract findById(id: string): Promise; abstract findByInstanceId(id: string): Promise; abstract find(options: FindWorkspacesOptions): Promise; abstract findWorkspacePortsAuthDataById(workspaceId: string): Promise; abstract findInstanceById(workspaceInstanceId: string): Promise; abstract findInstances(workspaceId: string): Promise; abstract findWorkspacesByUser(userId: string): Promise; abstract internalStoreInstance(instance: WorkspaceInstance): Promise; abstract findRegularRunningInstances(userId?: string): Promise; abstract findRunningInstancesWithWorkspaces(installation?: string, userId?: string): Promise; abstract findSessionsInPeriod(userId: string, periodStart: string, periodEnd: string): Promise; abstract findWorkspacesForGarbageCollection(minAgeInDays: number, limit: number): Promise; abstract findWorkspacesForContentDeletion(minSoftDeletedTimeInDays: number, limit: number): Promise; abstract findPrebuiltWorkspacesForGC(daysUnused: number, limit: number): Promise abstract findAllWorkspaceAndInstances(offset: number, limit: number, orderBy: keyof WorkspaceAndInstance, orderDir: "ASC" | "DESC", ownerId?: string, searchTerm?: string): Promise<{ total: number, rows: WorkspaceAndInstance[] }>; abstract findWorkspaceAndInstance(id: string): Promise; abstract findAllWorkspaces(offset: number, limit: number, orderBy: keyof Workspace, orderDir: "ASC" | "DESC", ownerId?: string, searchTerm?: string, minCreationTime?: Date, maxCreationDateTime?: Date, type?: WorkspaceType): Promise<{ total: number, rows: Workspace[] }>; abstract 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[] }>; public async transaction(code: (db: WorkspaceDB) => Promise): Promise { return code(this); } async storeInstance(instance: WorkspaceInstance): Promise { const inst = await this.internalStoreInstance(instance); return inst; } abstract updateLastHeartbeat(instanceId: string, userId: string, newHeartbeat: Date, wasClosed?: boolean): Promise; abstract getLastOwnerHeartbeatFor(instance: WorkspaceInstance): Promise<{ lastSeen: Date, wasClosed?: boolean} | undefined>; abstract getWorkspaceUsers(workspaceId: string, minLastSeen: number): Promise; abstract updateInstancePartial(instanceId: string, partial: DeepPartial): Promise; abstract findCurrentInstance(workspaceId: string): Promise; public async findRunningInstance(workspaceId: string): Promise { const instance = await this.findCurrentInstance(workspaceId) if (instance && instance.status.phase !== 'stopped') { return instance; } return undefined; } abstract isWhitelisted(repositoryUrl : string): Promise; abstract getFeaturedRepositories(): Promise[]>; abstract findSnapshotById(snapshotId: string): Promise; abstract findSnapshotsByWorkspaceId(workspaceId: string): Promise; abstract storeSnapshot(snapshot: Snapshot): Promise; abstract getTotalPrebuildUseSeconds(forDays: number): Promise; abstract storePrebuiltWorkspace(pws: PrebuiltWorkspace): Promise; abstract findPrebuiltWorkspaceByCommit(cloneURL: string, commit: string): Promise; abstract findPrebuildsWithWorkpace(cloneURL: string): Promise; abstract findPrebuildByWorkspaceID(wsid: string): Promise; abstract findPrebuildByID(pwsid: string): Promise; abstract countRunningPrebuilds(cloneURL: string): Promise; abstract findQueuedPrebuilds(cloneURL?: string): Promise; abstract attachUpdatableToPrebuild(pwsid: string, update: PrebuiltWorkspaceUpdatable): Promise; abstract findUpdatablesForPrebuild(pwsid: string): Promise; abstract markUpdatableResolved(updatableId: string): Promise; abstract getUnresolvedUpdatables(): Promise; abstract findLayoutDataByWorkspaceId(workspaceId: string): Promise; abstract storeLayoutData(layoutData: LayoutData): Promise; abstract hardDeleteWorkspace(workspaceID: string): Promise; }