diff --git a/components/gitpod-protocol/src/protocol.ts b/components/gitpod-protocol/src/protocol.ts index 087f03e68e7fea..713b1e641e3d97 100644 --- a/components/gitpod-protocol/src/protocol.ts +++ b/components/gitpod-protocol/src/protocol.ts @@ -298,6 +298,15 @@ export const WorkspaceFeatureFlags = { workspace_connection_limiting: undefined, }; export type NamedWorkspaceFeatureFlag = keyof typeof WorkspaceFeatureFlags; +export namespace NamedWorkspaceFeatureFlag { + export const WORKSPACE_PERSISTED_FEATTURE_FLAGS: NamedWorkspaceFeatureFlag[] = [ + "full_workspace_backup", + "persistent_volume_claim", + ]; + export function isWorkspacePersisted(ff: NamedWorkspaceFeatureFlag): boolean { + return WORKSPACE_PERSISTED_FEATTURE_FLAGS.includes(ff); + } +} export interface EnvVarWithValue { name: string; diff --git a/components/server/ee/src/prebuilds/prebuild-manager.ts b/components/server/ee/src/prebuilds/prebuild-manager.ts index f0d79ed07b5acb..00cb3905348a1d 100644 --- a/components/server/ee/src/prebuilds/prebuild-manager.ts +++ b/components/server/ee/src/prebuilds/prebuild-manager.ts @@ -249,10 +249,8 @@ export class PrebuildManager { } else { span.setTag("starting", true); const projectEnvVars = await projectEnvVarsPromise; - const usePVC = this.shouldUsePersistentVolumeClaim(project); await this.workspaceStarter.startWorkspace({ span }, workspace, user, [], projectEnvVars, { excludeFeatureFlags: ["full_workspace_backup"], - pvcEnabledForPrebuilds: usePVC, }); } @@ -322,13 +320,6 @@ export class PrebuildManager { return this.config.incrementalPrebuilds.repositoryPasslist.some((url) => trimRepoUrl(url) === repoUrl); } - protected shouldUsePersistentVolumeClaim(project?: Project): boolean { - if (project?.settings?.usePersistentVolumeClaim) { - return true; - } - return false; - } - async fetchConfig(ctx: TraceContext, user: User, context: CommitContext): Promise { const span = TraceContext.startSpan("fetchConfig", ctx); try { diff --git a/components/server/ee/src/workspace/workspace-factory.ts b/components/server/ee/src/workspace/workspace-factory.ts index bcf946aab51775..4eaee44cbd6f69 100644 --- a/components/server/ee/src/workspace/workspace-factory.ts +++ b/components/server/ee/src/workspace/workspace-factory.ts @@ -166,6 +166,18 @@ export class WorkspaceFactoryEE extends WorkspaceFactory { } ws.type = "prebuild"; ws.projectId = project?.id; + // Handle PVC propagation: + if (!project?.settings?.usePersistentVolumeClaim) { + if (ws.config._featureFlags) { + // If PVC is disabled, we want to make sure that we remove that feature flag (in case the user enabled it!) + // This is necessary to ensure if user has PVC enabled on their account, that they + // will not hijack prebuild with PVC and make everyone who use this prebuild to auto enroll into PVC feature. + ws.config._featureFlags = ws.config._featureFlags.filter((ff) => ff !== "persistent_volume_claim"); + } + } else { + // If PVC is enabled, we explicitly want all prebuilds to be stored that way. + ws.config._featureFlags = (ws.config._featureFlags || []).concat(["persistent_volume_claim"]); + } ws = await this.db.trace({ span }).store(ws); const pws = await this.db.trace({ span }).storePrebuiltWorkspace({ @@ -318,6 +330,11 @@ export class WorkspaceFactoryEE extends WorkspaceFactory { } } + // Special case for PVC: While it's a workspace-persisted feature flag, we support the upgrade path (non-pvc -> pvc), so we apply it here + if (user.featureFlags?.permanentWSFeatureFlags?.includes("persistent_volume_claim")) { + config._featureFlags = (config._featureFlags || []).concat(["persistent_volume_claim"]); + } + const id = await this.generateWorkspaceID(context); const newWs: Workspace = { id, diff --git a/components/server/ee/src/workspace/workspace-starter.ts b/components/server/ee/src/workspace/workspace-starter.ts index b4166830a789f7..17e57ae5668a5f 100644 --- a/components/server/ee/src/workspace/workspace-starter.ts +++ b/components/server/ee/src/workspace/workspace-starter.ts @@ -24,7 +24,6 @@ export class WorkspaceStarterEE extends WorkspaceStarter { user: User, excludeFeatureFlags: NamedWorkspaceFeatureFlag[], ideConfig: IDEConfig, - pvcEnabledForPrebuilds: boolean, ): Promise { const instance = await super.newInstance( ctx, @@ -33,7 +32,6 @@ export class WorkspaceStarterEE extends WorkspaceStarter { user, excludeFeatureFlags, ideConfig, - pvcEnabledForPrebuilds, ); return instance; diff --git a/components/server/src/workspace/config-provider.ts b/components/server/src/workspace/config-provider.ts index 487f10c9ab2ce6..b1ca5c558ad6ab 100644 --- a/components/server/src/workspace/config-provider.ts +++ b/components/server/src/workspace/config-provider.ts @@ -125,12 +125,8 @@ export class ConfigProvider { */ delete config._featureFlags; if (!!user.featureFlags) { - const workspacePersistedFlags: NamedWorkspaceFeatureFlag[] = [ - "full_workspace_backup", - "persistent_volume_claim", - ]; - config._featureFlags = workspacePersistedFlags.filter((f) => - (user.featureFlags!.permanentWSFeatureFlags || []).includes(f), + config._featureFlags = (user.featureFlags!.permanentWSFeatureFlags || []).filter( + NamedWorkspaceFeatureFlag.isWorkspacePersisted, ); } diff --git a/components/server/src/workspace/workspace-starter.ts b/components/server/src/workspace/workspace-starter.ts index d5c9f35da84f50..15bfc035025c7a 100644 --- a/components/server/src/workspace/workspace-starter.ts +++ b/components/server/src/workspace/workspace-starter.ts @@ -59,6 +59,8 @@ import { ProjectEnvVar, ImageBuildLogInfo, IDESettings, + WithReferrerContext, + EnvVarWithValue, } from "@gitpod/gitpod-protocol"; import { IAnalyticsWriter } from "@gitpod/gitpod-protocol/lib/analytics"; import { log } from "@gitpod/gitpod-protocol/lib/util/logging"; @@ -107,8 +109,6 @@ import { MessageBusIntegration } from "./messagebus-integration"; import * as path from "path"; import * as grpc from "@grpc/grpc-js"; import { IDEConfig, IDEConfigService } from "../ide-config"; -import { EnvVarWithValue } from "@gitpod/gitpod-protocol/src/protocol"; -import { WithReferrerContext } from "@gitpod/gitpod-protocol/lib/protocol"; import { IDEOption, IDEOptions } from "@gitpod/gitpod-protocol/lib/ide-protocol"; import { Deferred } from "@gitpod/gitpod-protocol/lib/util/deferred"; import { ExtendedUser } from "@gitpod/ws-manager/lib/constraints"; @@ -132,7 +132,6 @@ export interface StartWorkspaceOptions { rethrow?: boolean; forceDefaultImage?: boolean; excludeFeatureFlags?: NamedWorkspaceFeatureFlag[]; - pvcEnabledForPrebuilds?: boolean; } const MAX_INSTANCE_START_RETRIES = 2; @@ -364,7 +363,6 @@ export class WorkspaceStarter { user, options.excludeFeatureFlags || [], ideConfig, - options.pvcEnabledForPrebuilds || false, ), ); span.log({ newInstance: instance.id }); @@ -759,7 +757,6 @@ export class WorkspaceStarter { user: User, excludeFeatureFlags: NamedWorkspaceFeatureFlag[], ideConfig: IDEConfig, - pvcEnabledForPrebuilds: boolean, ): Promise { const span = TraceContext.startSpan("newInstance", ctx); //#endregion IDE resolution TODO(ak) move to IDE service @@ -829,7 +826,12 @@ export class WorkspaceStarter { let featureFlags: NamedWorkspaceFeatureFlag[] = workspace.config._featureFlags || []; featureFlags = featureFlags.concat(this.config.workspaceDefaults.defaultFeatureFlags); if (user.featureFlags && user.featureFlags.permanentWSFeatureFlags) { - featureFlags = featureFlags.concat(featureFlags, user.featureFlags.permanentWSFeatureFlags); + // Workspace-persisted feature flags are inherited from and controlled by workspace.config._featureFlags + // Make sure we do not overide them, here. + const nonWorkspacePersistentFeatureFlags = user.featureFlags.permanentWSFeatureFlags.filter( + (ff) => !NamedWorkspaceFeatureFlag.isWorkspacePersisted(ff), + ); + featureFlags = featureFlags.concat(featureFlags, nonWorkspacePersistentFeatureFlags); } // if the user has feature preview enabled, we need to add the respective feature flags. @@ -848,17 +850,6 @@ export class WorkspaceStarter { featureFlags = featureFlags.filter((f) => !excludeFeatureFlags.includes(f)); - if (workspace.type === "prebuild") { - if (pvcEnabledForPrebuilds === true) { - featureFlags = featureFlags.concat(["persistent_volume_claim"]); - } else { - // If PVC is disabled for prebuilds, we need to remove the PVC feature flag. - // This is necessary to ensure if user has PVC enabled on their account, that they - // will not hijack prebuild with PVC and make everyone who use this prebuild to auto enroll into PVC feature. - featureFlags = featureFlags.filter((f) => f !== "persistent_volume_claim"); - } - } - const userTeams = await this.teamDB.findTeamsByUser(user.id); const wsConnectionLimitingEnabled = await getExperimentsClientForBackend().getValueAsync( "workspace_connection_limiting",