From 4db80e793cc7d97efb2d6bd4cc6d70ff96efc5db Mon Sep 17 00:00:00 2001
From: Thomas Schubart
Date: Fri, 22 Jul 2022 11:49:06 +0000
Subject: [PATCH 1/5] [server] Add endpoint for retrieving workspace classes
---
components/gitpod-protocol/src/gitpod-service.ts | 3 +++
components/gitpod-protocol/src/workspace-class.ts | 13 +++++++++++++
components/server/src/auth/rate-limiter.ts | 1 +
.../server/src/workspace/gitpod-server-impl.ts | 15 +++++++++++++++
.../server/src/workspace/workspace-classes.ts | 9 +++++++++
5 files changed, 41 insertions(+)
create mode 100644 components/gitpod-protocol/src/workspace-class.ts
diff --git a/components/gitpod-protocol/src/gitpod-service.ts b/components/gitpod-protocol/src/gitpod-service.ts
index 8be6f5339a45de..f5d12018c6fa48 100644
--- a/components/gitpod-protocol/src/gitpod-service.ts
+++ b/components/gitpod-protocol/src/gitpod-service.ts
@@ -62,6 +62,7 @@ import { IDEServer } from "./ide-protocol";
import { InstallationAdminSettings, TelemetryData } from "./installation-admin-protocol";
import { Currency } from "./plans";
import { BillableSession } from "./usage";
+import { SupportedWorkspaceClass } from "./workspace-class";
export interface GitpodClient {
onInstanceUpdate(instance: WorkspaceInstance): void;
@@ -307,6 +308,8 @@ export interface GitpodServer extends JsonRpcServer, AdminServer,
* Frontend notifications
*/
getNotifications(): Promise;
+
+ getSupportedWorkspaceClasses(): Promise;
}
export interface RateLimiterError {
diff --git a/components/gitpod-protocol/src/workspace-class.ts b/components/gitpod-protocol/src/workspace-class.ts
new file mode 100644
index 00000000000000..68bca3ec8a79b8
--- /dev/null
+++ b/components/gitpod-protocol/src/workspace-class.ts
@@ -0,0 +1,13 @@
+/**
+ * Copyright (c) 2022 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.
+ */
+
+export interface SupportedWorkspaceClass {
+ id: string;
+ category: string;
+ displayName: string;
+ description: string;
+ powerups: number;
+}
diff --git a/components/server/src/auth/rate-limiter.ts b/components/server/src/auth/rate-limiter.ts
index ba0416f691bd22..b4bb8e3bff1391 100644
--- a/components/server/src/auth/rate-limiter.ts
+++ b/components/server/src/auth/rate-limiter.ts
@@ -220,6 +220,7 @@ function getConfig(config: RateLimiterConfig): RateLimiterConfig {
getSpendingLimitForTeam: { group: "default", points: 1 },
setSpendingLimitForTeam: { group: "default", points: 1 },
getNotifications: { group: "default", points: 1 },
+ getSupportedWorkspaceClasses: { group: "default", points: 1 },
};
return {
diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts
index 7767c15a332d01..2f5db15d7f2037 100644
--- a/components/server/src/workspace/gitpod-server-impl.ts
+++ b/components/server/src/workspace/gitpod-server-impl.ts
@@ -115,6 +115,7 @@ import {
RemotePageMessage,
RemoteTrackMessage,
} from "@gitpod/gitpod-protocol/lib/analytics";
+import { SupportedWorkspaceClass } from "@gitpod/gitpod-protocol/lib/workspace-class";
import { ImageBuilderClientProvider, LogsRequest } from "@gitpod/image-builder/lib";
import { WorkspaceManagerClientProvider } from "@gitpod/ws-manager/lib/client-provider";
import {
@@ -3042,6 +3043,20 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
return ideConfig.ideOptions;
}
+ async getSupportedWorkspaceClasses(ctx: TraceContext): Promise {
+ let classes = this.config.workspaceClasses
+ .filter((c) => !c.deprecated)
+ .map((c) => ({
+ id: c.id,
+ category: c.category,
+ displayName: c.displayName,
+ description: c.description,
+ powerups: c.powerups,
+ }));
+
+ return classes;
+ }
+
//#region gitpod.io concerns
//
async adminGetAccountStatement(ctx: TraceContext, userId: string): Promise {
diff --git a/components/server/src/workspace/workspace-classes.ts b/components/server/src/workspace/workspace-classes.ts
index ebbaa6c75dd25e..d8bf42d754d9d8 100644
--- a/components/server/src/workspace/workspace-classes.ts
+++ b/components/server/src/workspace/workspace-classes.ts
@@ -15,9 +15,18 @@ export interface WorkspaceClassConfig {
// Is the "default" class. The config is validated to only every have exactly _one_ default class.
isDefault: boolean;
+ // Identifies which category this class belongs to e.g. general purpose
+ category: string;
+
// The string we display to users in the UI
displayName: string;
+ // The description for the workspace class
+ description: string;
+
+ // The "power level" of the workspace class
+ powerups: number;
+
// Whether or not to:
// - offer users this Workspace class for selection
// - 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.
From 1e7514e949331ec0dbace0e394de85bfb26ac26e Mon Sep 17 00:00:00 2001
From: Thomas Schubart
Date: Fri, 22 Jul 2022 12:42:52 +0000
Subject: [PATCH 2/5] [dashboard] Get classes from server
---
.../dashboard/src/settings/selectClass.tsx | 51 ++++++++++---------
1 file changed, 28 insertions(+), 23 deletions(-)
diff --git a/components/dashboard/src/settings/selectClass.tsx b/components/dashboard/src/settings/selectClass.tsx
index 17ec3f0223aabc..dd67bac8692609 100644
--- a/components/dashboard/src/settings/selectClass.tsx
+++ b/components/dashboard/src/settings/selectClass.tsx
@@ -4,12 +4,13 @@
* See License-AGPL.txt in the project root for license information.
*/
-import { useContext, useState } from "react";
+import { useContext, useEffect, useState } from "react";
import { getGitpodService } from "../service/service";
import { UserContext } from "../user-context";
import { trackEvent } from "../Analytics";
import { WorkspaceClasses } from "@gitpod/gitpod-protocol";
import WorkspaceClass from "../components/WorkspaceClass";
+import { SupportedWorkspaceClass } from "@gitpod/gitpod-protocol/lib/workspace-class";
interface SelectWorkspaceClassProps {
enabled: boolean;
@@ -18,12 +19,10 @@ interface SelectWorkspaceClassProps {
export default function SelectWorkspaceClass(props: SelectWorkspaceClassProps) {
const { user } = useContext(UserContext);
- const [workspaceClass, setWorkspaceClass] = useState(
- user?.additionalData?.workspaceClasses?.regular || "g1-standard",
- );
+ const [workspaceClass, setWorkspaceClass] = useState(user?.additionalData?.workspaceClasses?.regular || "");
const actuallySetWorkspaceClass = async (value: string) => {
const additionalData = user?.additionalData || {};
- const prevWorkspaceClass = additionalData?.workspaceClasses?.regular || "g1-standard";
+ const prevWorkspaceClass = additionalData?.workspaceClasses?.regular || "";
const workspaceClasses = (additionalData?.workspaceClasses || {}) as WorkspaceClasses;
workspaceClasses.regular = value;
workspaceClasses.prebuild = value;
@@ -38,6 +37,17 @@ export default function SelectWorkspaceClass(props: SelectWorkspaceClassProps) {
}
};
+ const [supportedClasses, setSupportedClasses] = useState([]);
+
+ useEffect(() => {
+ const fetchClasses = async () => {
+ const classes = await getGitpodService().server.getSupportedWorkspaceClasses();
+ setSupportedClasses(classes);
+ };
+
+ fetchClasses().catch(console.error);
+ }, []);
+
if (!props.enabled) {
return ;
} else {
@@ -48,24 +58,19 @@ export default function SelectWorkspaceClass(props: SelectWorkspaceClassProps) {
Choose the workspace machine type for your workspaces.
- actuallySetWorkspaceClass("g1-standard")}
- category="GENERAL PURPOSE"
- friendlyName="Standard"
- description="Up to 4 vCPU, 8GB memory, 30GB disk"
- powerUps={1}
- />
- actuallySetWorkspaceClass("g1-large")}
- category="GENERAL PURPOSE"
- friendlyName="Large"
- description="Up to 8 vCPU, 16GB memory, 50GB disk"
- powerUps={2}
- />
+ {supportedClasses.map((c) => {
+ return (
+ actuallySetWorkspaceClass(c.id)}
+ category={c.category}
+ friendlyName={c.displayName}
+ description={c.description}
+ powerUps={c.powerups}
+ />
+ );
+ })}
);
From 1299ba8ed4d7c4a7a0b78556468db2158884433b Mon Sep 17 00:00:00 2001
From: Thomas Schubart
Date: Fri, 22 Jul 2022 12:54:54 +0000
Subject: [PATCH 3/5] [installer] Extend workspace class info
---
.../pkg/components/server/configmap.go | 9 ++++++++-
.../installer/pkg/components/server/types.go | 19 ++++++++++++++-----
.../config/v1/experimental/experimental.go | 3 +++
3 files changed, 25 insertions(+), 6 deletions(-)
diff --git a/install/installer/pkg/components/server/configmap.go b/install/installer/pkg/components/server/configmap.go
index db0d8054f012f2..c960fb63c36d2a 100644
--- a/install/installer/pkg/components/server/configmap.go
+++ b/install/installer/pkg/components/server/configmap.go
@@ -8,6 +8,7 @@ import (
"fmt"
"net"
"strconv"
+ "strings"
"github.com/gitpod-io/gitpod/installer/pkg/common"
"github.com/gitpod-io/gitpod/installer/pkg/components/usage"
@@ -137,7 +138,10 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
workspaceClasses := []WorkspaceClass{
{
Id: config.DefaultWorkspaceClass,
- DisplayName: config.DefaultWorkspaceClass,
+ Category: GeneralPurpose,
+ DisplayName: strings.Title(config.DefaultWorkspaceClass),
+ Description: "Default workspace class",
+ PowerUps: 1,
IsDefault: true,
Deprecated: false,
},
@@ -148,7 +152,10 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) {
for _, cl := range cfg.WebApp.WorkspaceClasses {
class := WorkspaceClass{
Id: cl.Id,
+ Category: WorkspaceClassCategory(cl.Category),
DisplayName: cl.DisplayName,
+ Description: cl.Description,
+ PowerUps: cl.PowerUps,
IsDefault: cl.IsDefault,
Deprecated: cl.Deprecated,
Marker: cl.Marker,
diff --git a/install/installer/pkg/components/server/types.go b/install/installer/pkg/components/server/types.go
index b5612c96347ccb..ee634a99714ae0 100644
--- a/install/installer/pkg/components/server/types.go
+++ b/install/installer/pkg/components/server/types.go
@@ -129,11 +129,14 @@ type WorkspaceDefaults struct {
}
type WorkspaceClass struct {
- Id string `json:"id"`
- DisplayName string `json:"displayName"`
- IsDefault bool `json:"isDefault"`
- Deprecated bool `json:"deprecated"`
- Marker map[string]bool `json:"marker,omitempty"`
+ Id string `json:"id"`
+ Category WorkspaceClassCategory `json:"category"`
+ DisplayName string `json:"displayName"`
+ Description string `json:"description"`
+ PowerUps uint32 `json:"powerups"`
+ IsDefault bool `json:"isDefault"`
+ Deprecated bool `json:"deprecated"`
+ Marker map[string]bool `json:"marker,omitempty"`
}
type NamedWorkspaceFeatureFlag string
@@ -142,3 +145,9 @@ const (
NamedWorkspaceFeatureFlagFullWorkspaceBackup NamedWorkspaceFeatureFlag = "full_workspace_backup"
NamedWorkspaceFeatureFlagFixedResources NamedWorkspaceFeatureFlag = "fixed_resources"
)
+
+type WorkspaceClassCategory string
+
+const (
+ GeneralPurpose WorkspaceClassCategory = "GENERAL PURPOSE"
+)
diff --git a/install/installer/pkg/config/v1/experimental/experimental.go b/install/installer/pkg/config/v1/experimental/experimental.go
index ac6c3e25055c9f..864a3d78768e55 100644
--- a/install/installer/pkg/config/v1/experimental/experimental.go
+++ b/install/installer/pkg/config/v1/experimental/experimental.go
@@ -209,7 +209,10 @@ type UsageConfig struct {
type WebAppWorkspaceClass struct {
Id string `json:"id"`
+ Category string `json:"category"`
DisplayName string `json:"displayName"`
+ Description string `json:"description"`
+ PowerUps uint32 `json:"powerups"`
IsDefault bool `json:"isDefault"`
Deprecated bool `json:"deprecated"`
Marker map[string]bool `json:"marker,omitempty"`
From 29778a56208013441ae03e17ba3ba707345af8f7 Mon Sep 17 00:00:00 2001
From: Thomas Schubart
Date: Fri, 22 Jul 2022 15:17:07 +0000
Subject: [PATCH 4/5] [dashboard] Update service mocks
---
.../dashboard/src/service/service-mock.ts | 53 +++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/components/dashboard/src/service/service-mock.ts b/components/dashboard/src/service/service-mock.ts
index d416d04d04409e..ac7841ed8c1639 100644
--- a/components/dashboard/src/service/service-mock.ts
+++ b/components/dashboard/src/service/service-mock.ts
@@ -210,6 +210,59 @@ const gitpodServiceMock = createServiceMock({
onDidCloseConnection: Event.None,
trackEvent: async (event) => {},
trackLocation: async (event) => {},
+ getSupportedWorkspaceClasses: async () => {
+ return [
+ {
+ id: "g1-standard",
+ category: "GENERAL PURPOSE",
+ displayName: "Standard",
+ description: "Up to 4 vCPU, 8GB memory, 30GB disk",
+ powerups: 1,
+ },
+ {
+ id: "g1-large",
+ category: "GENERAL PURPOSE",
+ displayName: "Large",
+ description: "Up to 8 vCPU, 16GB memory, 50GB disk",
+ powerups: 2,
+ },
+ ];
+ },
+ getShowPaymentUI: async () => {
+ return false;
+ },
+ getClientRegion: async () => {
+ return "europe-west-1";
+ },
+ isStudent: async () => {
+ return false;
+ },
+ isChargebeeCustomer: async () => {
+ return false;
+ },
+ getSuggestedContextURLs: async () => {
+ return [];
+ },
+ getIDEOptions: async () => {
+ return {
+ defaultDesktopIde: "code-desktop",
+ defaultIde: "code",
+ options: {
+ code: {
+ title: "VS Code",
+ type: "browser",
+ logo: "",
+ image: "eu.gcr.io/gitpod-core-dev/build/ide/code:commit-050c611f28564c6c7b1e58db470f07997dfb4730",
+ },
+ "code-desktop": {
+ title: "VS Code",
+ type: "desktop",
+ logo: "",
+ image: "eu.gcr.io/gitpod-core-dev/build/ide/code-desktop:commit-9b29fc94cc1f0c776ef74f60dc3a7ce68d41bdbe",
+ },
+ },
+ };
+ },
});
export { gitpodServiceMock };
From 15875d248c04a492860b5b6d5c0e9aaff69bcb9c Mon Sep 17 00:00:00 2001
From: Thomas Schubart
Date: Mon, 25 Jul 2022 11:53:29 +0000
Subject: [PATCH 5/5] [dashboard] Select default class if nothing is selected
---
components/dashboard/src/service/service-mock.ts | 2 ++
components/dashboard/src/settings/selectClass.tsx | 6 +++++-
components/gitpod-protocol/src/workspace-class.ts | 1 +
components/server/src/workspace/gitpod-server-impl.ts | 1 +
4 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/components/dashboard/src/service/service-mock.ts b/components/dashboard/src/service/service-mock.ts
index ac7841ed8c1639..22d395d16c5705 100644
--- a/components/dashboard/src/service/service-mock.ts
+++ b/components/dashboard/src/service/service-mock.ts
@@ -218,6 +218,7 @@ const gitpodServiceMock = createServiceMock({
displayName: "Standard",
description: "Up to 4 vCPU, 8GB memory, 30GB disk",
powerups: 1,
+ isDefault: true,
},
{
id: "g1-large",
@@ -225,6 +226,7 @@ const gitpodServiceMock = createServiceMock({
displayName: "Large",
description: "Up to 8 vCPU, 16GB memory, 50GB disk",
powerups: 2,
+ isDefault: false,
},
];
},
diff --git a/components/dashboard/src/settings/selectClass.tsx b/components/dashboard/src/settings/selectClass.tsx
index dd67bac8692609..5bc5988a78525d 100644
--- a/components/dashboard/src/settings/selectClass.tsx
+++ b/components/dashboard/src/settings/selectClass.tsx
@@ -43,10 +43,14 @@ export default function SelectWorkspaceClass(props: SelectWorkspaceClassProps) {
const fetchClasses = async () => {
const classes = await getGitpodService().server.getSupportedWorkspaceClasses();
setSupportedClasses(classes);
+
+ if (!workspaceClass) {
+ setWorkspaceClass(supportedClasses.find((c) => c.isDefault)?.id || "");
+ }
};
fetchClasses().catch(console.error);
- }, []);
+ });
if (!props.enabled) {
return ;
diff --git a/components/gitpod-protocol/src/workspace-class.ts b/components/gitpod-protocol/src/workspace-class.ts
index 68bca3ec8a79b8..3128db9da1060b 100644
--- a/components/gitpod-protocol/src/workspace-class.ts
+++ b/components/gitpod-protocol/src/workspace-class.ts
@@ -10,4 +10,5 @@ export interface SupportedWorkspaceClass {
displayName: string;
description: string;
powerups: number;
+ isDefault: boolean;
}
diff --git a/components/server/src/workspace/gitpod-server-impl.ts b/components/server/src/workspace/gitpod-server-impl.ts
index 2f5db15d7f2037..eddc1b81770d7d 100644
--- a/components/server/src/workspace/gitpod-server-impl.ts
+++ b/components/server/src/workspace/gitpod-server-impl.ts
@@ -3052,6 +3052,7 @@ export class GitpodServerImpl implements GitpodServerWithTracing, Disposable {
displayName: c.displayName,
description: c.description,
powerups: c.powerups,
+ isDefault: c.isDefault,
}));
return classes;