Skip to content

Commit d707320

Browse files
geroplFuristo
authored andcommitted
[server] Use WorkspaceClassesConfig
1 parent ea12ee0 commit d707320

File tree

4 files changed

+121
-36
lines changed

4 files changed

+121
-36
lines changed

components/server/src/config.ts

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import * as fs from "fs";
1616
import * as yaml from "js-yaml";
1717
import { log } from "@gitpod/gitpod-protocol/lib/util/logging";
1818
import { filePathTelepresenceAware } from "@gitpod/gitpod-protocol/lib/env";
19+
import { WorkspaceClasses, WorkspaceClassesConfig } from "./workspace/workspace-classes";
1920

2021
export const Config = Symbol("Config");
2122
export type Config = Omit<
@@ -55,27 +56,6 @@ export interface WorkspaceGarbageCollection {
5556
contentChunkLimit: number;
5657
}
5758

58-
type WorkspaceClassesConfig = [WorkspaceClassConfig];
59-
60-
interface WorkspaceClassConfig {
61-
// The technical string we use to identify the class with internally
62-
id: string;
63-
64-
// Is the "default" class. The config is validated to only every have exactly _one_ default class.
65-
isDefault: boolean;
66-
67-
// The string we display to users in the UI
68-
displayName: string;
69-
70-
// Whether or not to:
71-
// - offer users this Workspace class for selection
72-
// - use this class to start workspaces with. If a user has a class marked like this configured and starts
73-
deprecated: boolean;
74-
75-
// The price for this workspace class in "credits per minute"
76-
creditsPerMinute: number;
77-
}
78-
7959
/**
8060
* This is the config shape as found in the configuration file, e.g. server-configmap.yaml
8161
*/
@@ -293,13 +273,7 @@ export namespace ConfigFile {
293273
}
294274
}
295275

296-
let defaultClasses = config.workspaceClasses
297-
.map((c) => (c.isDefault ? 1 : 0))
298-
.reduce((acc: number, isDefault: number) => (acc + isDefault) as number, 0);
299-
300-
if (defaultClasses != 1) {
301-
throw new Error("exactly one default workspace class needs to be configured, not " + defaultClasses);
302-
}
276+
WorkspaceClasses.validate(config.workspaceClasses);
303277

304278
return {
305279
...config,
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* Copyright (c) 2022 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 { log } from "@gitpod/gitpod-protocol/lib/util/logging";
8+
9+
export type WorkspaceClassesConfig = [WorkspaceClassConfig];
10+
11+
export interface WorkspaceClassConfig {
12+
// The technical string we use to identify the class with internally
13+
id: string;
14+
15+
// Is the "default" class. The config is validated to only every have exactly _one_ default class.
16+
isDefault: boolean;
17+
18+
// The string we display to users in the UI
19+
displayName: string;
20+
21+
// Whether or not to:
22+
// - offer users this Workspace class for selection
23+
// - use this class to start workspaces with. If a user has a class marked like this configured and starts a workspace they get the default class instead.
24+
deprecated: boolean;
25+
26+
// Marks this class to have special semantics
27+
marker?: {
28+
// Marks this class as the one that users marked with "GetMoreResources" receive
29+
moreResources: boolean;
30+
};
31+
}
32+
33+
export namespace WorkspaceClasses {
34+
/**
35+
* @param workspaceClasses
36+
* @return The WorkspaceClass ID of the first class that is marked with "moreResources" (and not deprecated). Falls back to "getDefaultId()".
37+
*/
38+
export function getMoreResourcesIdOrDefault(workspaceClasses: WorkspaceClassesConfig): string {
39+
const moreResources = workspaceClasses.filter((c) => !c.deprecated).find((c) => !!c.marker?.moreResources);
40+
if (moreResources) {
41+
return moreResources.id;
42+
}
43+
44+
// fallback: default
45+
return getDefaultId(workspaceClasses);
46+
}
47+
48+
/**
49+
* @param workspaceClasses
50+
* @return The WorkspaceClass ID of the "default" class
51+
*/
52+
export function getDefaultId(workspaceClasses: WorkspaceClassesConfig): string {
53+
validate(workspaceClasses);
54+
55+
return workspaceClasses.filter((c) => !c.deprecated).find((c) => c.isDefault)!.id;
56+
}
57+
58+
/**
59+
* Checks that the given workspaceClass is:
60+
* - still configured
61+
* - not deprecated
62+
* If any of that is the case, it returns the default class
63+
*
64+
* @param workspaceClasses
65+
* @param previousWorkspaceClass
66+
*/
67+
export function getPreviousOrDefault(
68+
workspaceClasses: WorkspaceClassesConfig,
69+
previousWorkspaceClass: string | undefined,
70+
): string {
71+
if (!previousWorkspaceClass) {
72+
return getDefaultId(workspaceClasses);
73+
}
74+
75+
const config = workspaceClasses.find((c) => c.id === previousWorkspaceClass);
76+
if (!config) {
77+
log.error(
78+
`Found previous instance with workspace class '${previousWorkspaceClass}' which is no longer configured! Falling back to default class.`,
79+
{ workspaceClasses },
80+
);
81+
return getDefaultId(workspaceClasses);
82+
}
83+
if (config.deprecated) {
84+
log.info(
85+
`Found previous instance with workspace class '${previousWorkspaceClass}' which is deprecated. Falling back to default class.`,
86+
{ workspaceClasses },
87+
);
88+
return getDefaultId(workspaceClasses);
89+
}
90+
return config.id;
91+
}
92+
93+
export function validate(workspaceClasses: WorkspaceClassesConfig): void {
94+
const defaultClasses = workspaceClasses
95+
.filter((c) => !c.deprecated)
96+
.map((c) => (c.isDefault ? 1 : 0))
97+
.reduce((acc: number, isDefault: number) => (acc + isDefault) as number, 0);
98+
99+
if (defaultClasses !== 1) {
100+
throw new Error(
101+
"Exactly one default workspace class needs to be configured:" + JSON.stringify(defaultClasses),
102+
);
103+
}
104+
}
105+
}

components/server/src/workspace/workspace-starter.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ import { ContextParser } from "./context-parser-service";
117117
import { IDEService } from "../ide-service";
118118
import { WorkspaceClusterImagebuilderClientProvider } from "./workspace-cluster-imagebuilder-client-provider";
119119
import { getExperimentsClientForBackend } from "@gitpod/gitpod-protocol/lib/experiments/configcat-server";
120+
import { WorkspaceClasses } from "./workspace-classes";
120121

121122
export interface StartWorkspaceOptions {
122123
rethrow?: boolean;
@@ -801,13 +802,16 @@ export class WorkspaceStarter {
801802
}
802803

803804
if (!workspaceClass) {
804-
workspaceClass = this.config.workspaceClasses.find((cl) => cl.isDefault)?.id ?? "";
805+
workspaceClass = WorkspaceClasses.getDefaultId(this.config.workspaceClasses);
805806
if (await this.userService.userGetsMoreResources(user)) {
806-
workspaceClass = this.config.workspaceClasses.find((cl) => !cl.isDefault)?.id ?? "";
807+
workspaceClass = WorkspaceClasses.getMoreResourcesIdOrDefault(this.config.workspaceClasses);
807808
}
808809
}
809810
} else {
810-
workspaceClass = previousInstance.workspaceClass;
811+
workspaceClass = WorkspaceClasses.getPreviousOrDefault(
812+
this.config.workspaceClasses,
813+
previousInstance.workspaceClass,
814+
);
811815
}
812816
}
813817

@@ -1398,6 +1402,7 @@ export class WorkspaceStarter {
13981402
});
13991403
let workspaceClass;
14001404
if (!classesEnabled) {
1405+
// This is branch is not relevant once we roll out WorkspaceClasses, so we don't try to integrate these old classes into our model
14011406
workspaceClass = "default";
14021407
if (await this.userService.userGetsMoreResources(user)) {
14031408
workspaceClass = "gitpodio-internal-xl";

install/installer/pkg/components/server/types.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,12 @@ type WorkspaceDefaults struct {
136136
}
137137

138138
type WorkspaceClass struct {
139-
Id string `json:"id"`
140-
DisplayName string `json:"displayName"`
141-
IsDefault bool `json:"isDefault"`
142-
Deprecated bool `json:"deprecated"`
143-
CreditsPerMinute int32 `json:"creditsPerMinute"`
139+
Id string `json:"id"`
140+
DisplayName string `json:"displayName"`
141+
IsDefault bool `json:"isDefault"`
142+
Deprecated bool `json:"deprecated"`
143+
CreditsPerMinute int32 `json:"creditsPerMinute"`
144+
Marker map[string]bool `json:"marker,omitempty"`
144145
}
145146

146147
type NamedWorkspaceFeatureFlag string

0 commit comments

Comments
 (0)