Path: blob/main/components/gitpod-protocol/src/workspace-cluster.ts
2498 views
/**1* Copyright (c) 2020 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 * as fs from "fs";7import { filePathTelepresenceAware } from "./env";8import { DeepPartial } from "./util/deep-partial";9import { PermissionName } from "./permission";1011const workspaceRegions = ["europe", "north-america", "south-america", "africa", "asia", ""] as const;12export type WorkspaceRegion = typeof workspaceRegions[number];1314export function isWorkspaceRegion(s: string): s is WorkspaceRegion {15// eslint-disable-next-line @typescript-eslint/no-unsafe-argument16return workspaceRegions.indexOf(s as any) !== -1;17}1819export interface WorkspaceCluster {20// Name of the workspace cluster.21// This is the string set in each22// Must be identical to the installationShortname of the cluster it represents!23name: string;2425// The name of the region this cluster belongs to. E.g. europe or north-america26// The name can be at most 60 characters.27region: WorkspaceRegion;2829// URL of the cluster's ws-manager API30url: string;3132// TLS contains the keys and certificates necessary to use mTLS between server and clients33tls?: TLSConfig;3435// Current state of the cluster36state: WorkspaceClusterState;3738// Maximum value score can reach for this cluster39maxScore: number;4041// Score used for cluster selection when starting workspace instances42score: number;4344// True if this bridge should control this cluster45govern: boolean;4647// An optional set of constraints that limit who can start workspaces on the cluster48admissionConstraints?: AdmissionConstraint[];4950// The classes of workspaces that can be started on this cluster51availableWorkspaceClasses?: WorkspaceClass[];5253// The class of workspaces that should be started on this cluster by default54preferredWorkspaceClass?: string;55}5657export namespace WorkspaceCluster {58export function preferredWorkspaceClass(cluster: WorkspaceCluster): WorkspaceClass | undefined {59return (cluster.availableWorkspaceClasses || []).find((c) => c.id === cluster.preferredWorkspaceClass);60}61}6263export type WorkspaceClusterState = "available" | "cordoned" | "draining";64export interface TLSConfig {65// the CA shared between client and server (base64 encoded)66ca: string;67// the private key (base64 encoded)68key: string;69// the certificate signed with the shared CA (base64 encoded)70crt: string;71}72export namespace TLSConfig {73export const loadFromBase64File = (path: string): string =>74fs.readFileSync(filePathTelepresenceAware(path)).toString("base64");75}76export type WorkspaceClusterWoTLS = Omit<WorkspaceCluster, "tls">;77export type WorkspaceManagerConnectionInfo = Pick<WorkspaceCluster, "name" | "url" | "tls">;7879export type AdmissionConstraint =80| AdmissionConstraintFeaturePreview81| AdmissionConstraintHasPermission82| AdmissionConstraintHasClass;83export type AdmissionConstraintFeaturePreview = { type: "has-feature-preview" };84export type AdmissionConstraintHasPermission = { type: "has-permission"; permission: PermissionName };85export type AdmissionConstraintHasClass = { type: "has-class"; id: string; displayName: string };8687export namespace AdmissionConstraint {88export function is(o: any): o is AdmissionConstraint {89return !!o && "type" in o;90}91export function isHasPermissionConstraint(o: any): o is AdmissionConstraintHasPermission {92return is(o) && o.type === "has-permission";93}94export function hasPermission(ac: AdmissionConstraint, permission: PermissionName): boolean {95return isHasPermissionConstraint(ac) && ac.permission === permission;96}97}9899export interface WorkspaceClass {100// id is a unique identifier (within the cluster) of this workspace class101id: string;102103// The string we display to users in the UI104displayName: string;105106// The description of this workspace class107description: string;108109// The cost of running a workspace of this class per minute expressed in credits110creditsPerMinute: number;111}112113export const WorkspaceClusterDB = Symbol("WorkspaceClusterDB");114export interface WorkspaceClusterDB {115/**116* Stores the given WorkspaceCluster to the cluster-local DB in a consistent manner.117* If there already is an entry with the same name it's merged and updated with the given state.118* @param cluster119*/120save(cluster: WorkspaceCluster): Promise<void>;121122/**123* Deletes the cluster identified by this name, if any.124* @param name125*/126deleteByName(name: string): Promise<void>;127128/**129* Finds a WorkspaceCluster with the given name. If there is none, `undefined` is returned.130* @param name131*/132findByName(name: string): Promise<WorkspaceCluster | undefined>;133134/**135* Lists all WorkspaceClusterWoTls for which the given predicate is true (does not return TLS for size/speed concerns)136* @param predicate137*/138findFiltered(predicate: WorkspaceClusterFilter): Promise<WorkspaceClusterWoTLS[]>;139}140141export type WorkspaceClusterFilter = DeepPartial<142Pick<WorkspaceCluster, "name" | "state" | "govern" | "url" | "region">143> &144Partial<{ minScore: number }>;145146147