Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/gitpod-protocol/src/workspace-cluster.ts
2498 views
1
/**
2
* Copyright (c) 2020 Gitpod GmbH. All rights reserved.
3
* Licensed under the GNU Affero General Public License (AGPL).
4
* See License.AGPL.txt in the project root for license information.
5
*/
6
7
import * as fs from "fs";
8
import { filePathTelepresenceAware } from "./env";
9
import { DeepPartial } from "./util/deep-partial";
10
import { PermissionName } from "./permission";
11
12
const workspaceRegions = ["europe", "north-america", "south-america", "africa", "asia", ""] as const;
13
export type WorkspaceRegion = typeof workspaceRegions[number];
14
15
export function isWorkspaceRegion(s: string): s is WorkspaceRegion {
16
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
17
return workspaceRegions.indexOf(s as any) !== -1;
18
}
19
20
export interface WorkspaceCluster {
21
// Name of the workspace cluster.
22
// This is the string set in each
23
// Must be identical to the installationShortname of the cluster it represents!
24
name: string;
25
26
// The name of the region this cluster belongs to. E.g. europe or north-america
27
// The name can be at most 60 characters.
28
region: WorkspaceRegion;
29
30
// URL of the cluster's ws-manager API
31
url: string;
32
33
// TLS contains the keys and certificates necessary to use mTLS between server and clients
34
tls?: TLSConfig;
35
36
// Current state of the cluster
37
state: WorkspaceClusterState;
38
39
// Maximum value score can reach for this cluster
40
maxScore: number;
41
42
// Score used for cluster selection when starting workspace instances
43
score: number;
44
45
// True if this bridge should control this cluster
46
govern: boolean;
47
48
// An optional set of constraints that limit who can start workspaces on the cluster
49
admissionConstraints?: AdmissionConstraint[];
50
51
// The classes of workspaces that can be started on this cluster
52
availableWorkspaceClasses?: WorkspaceClass[];
53
54
// The class of workspaces that should be started on this cluster by default
55
preferredWorkspaceClass?: string;
56
}
57
58
export namespace WorkspaceCluster {
59
export function preferredWorkspaceClass(cluster: WorkspaceCluster): WorkspaceClass | undefined {
60
return (cluster.availableWorkspaceClasses || []).find((c) => c.id === cluster.preferredWorkspaceClass);
61
}
62
}
63
64
export type WorkspaceClusterState = "available" | "cordoned" | "draining";
65
export interface TLSConfig {
66
// the CA shared between client and server (base64 encoded)
67
ca: string;
68
// the private key (base64 encoded)
69
key: string;
70
// the certificate signed with the shared CA (base64 encoded)
71
crt: string;
72
}
73
export namespace TLSConfig {
74
export const loadFromBase64File = (path: string): string =>
75
fs.readFileSync(filePathTelepresenceAware(path)).toString("base64");
76
}
77
export type WorkspaceClusterWoTLS = Omit<WorkspaceCluster, "tls">;
78
export type WorkspaceManagerConnectionInfo = Pick<WorkspaceCluster, "name" | "url" | "tls">;
79
80
export type AdmissionConstraint =
81
| AdmissionConstraintFeaturePreview
82
| AdmissionConstraintHasPermission
83
| AdmissionConstraintHasClass;
84
export type AdmissionConstraintFeaturePreview = { type: "has-feature-preview" };
85
export type AdmissionConstraintHasPermission = { type: "has-permission"; permission: PermissionName };
86
export type AdmissionConstraintHasClass = { type: "has-class"; id: string; displayName: string };
87
88
export namespace AdmissionConstraint {
89
export function is(o: any): o is AdmissionConstraint {
90
return !!o && "type" in o;
91
}
92
export function isHasPermissionConstraint(o: any): o is AdmissionConstraintHasPermission {
93
return is(o) && o.type === "has-permission";
94
}
95
export function hasPermission(ac: AdmissionConstraint, permission: PermissionName): boolean {
96
return isHasPermissionConstraint(ac) && ac.permission === permission;
97
}
98
}
99
100
export interface WorkspaceClass {
101
// id is a unique identifier (within the cluster) of this workspace class
102
id: string;
103
104
// The string we display to users in the UI
105
displayName: string;
106
107
// The description of this workspace class
108
description: string;
109
110
// The cost of running a workspace of this class per minute expressed in credits
111
creditsPerMinute: number;
112
}
113
114
export const WorkspaceClusterDB = Symbol("WorkspaceClusterDB");
115
export interface WorkspaceClusterDB {
116
/**
117
* Stores the given WorkspaceCluster to the cluster-local DB in a consistent manner.
118
* If there already is an entry with the same name it's merged and updated with the given state.
119
* @param cluster
120
*/
121
save(cluster: WorkspaceCluster): Promise<void>;
122
123
/**
124
* Deletes the cluster identified by this name, if any.
125
* @param name
126
*/
127
deleteByName(name: string): Promise<void>;
128
129
/**
130
* Finds a WorkspaceCluster with the given name. If there is none, `undefined` is returned.
131
* @param name
132
*/
133
findByName(name: string): Promise<WorkspaceCluster | undefined>;
134
135
/**
136
* Lists all WorkspaceClusterWoTls for which the given predicate is true (does not return TLS for size/speed concerns)
137
* @param predicate
138
*/
139
findFiltered(predicate: WorkspaceClusterFilter): Promise<WorkspaceClusterWoTLS[]>;
140
}
141
142
export type WorkspaceClusterFilter = DeepPartial<
143
Pick<WorkspaceCluster, "name" | "state" | "govern" | "url" | "region">
144
> &
145
Partial<{ minScore: number }>;
146
147