diff --git a/WORKSPACE.yaml b/WORKSPACE.yaml index 8469215ada0946..0b5eef9966332a 100644 --- a/WORKSPACE.yaml +++ b/WORKSPACE.yaml @@ -7,7 +7,7 @@ defaultArgs: publishToNPM: true publishToJBMarketplace: true localAppVersion: unknown - codeCommit: 05e928f3d900f8b27574d846d46d2ca4a10c2ffd + codeCommit: 8ee664e58e31e81e9dd9da10fb31809e249f0d61 codeVersion: 1.75.0 codeQuality: stable noVerifyJBPlugin: false diff --git a/components/gitpod-cli/pkg/supervisor/client.go b/components/gitpod-cli/pkg/supervisor/client.go index 815f9feb0197a7..034ea6c3cb6652 100644 --- a/components/gitpod-cli/pkg/supervisor/client.go +++ b/components/gitpod-cli/pkg/supervisor/client.go @@ -30,7 +30,7 @@ type SupervisorClientOption struct { Address string } -func New(ctx context.Context, options ...SupervisorClientOption) (*SupervisorClient, error) { +func New(ctx context.Context, options ...*SupervisorClientOption) (*SupervisorClient, error) { address := util.GetSupervisorAddress() for _, option := range options { if option.Address != "" { diff --git a/components/gitpod-protocol/src/util/gitpod-host-url.ts b/components/gitpod-protocol/src/util/gitpod-host-url.ts index 2fe9c146c04978..0deb76ed0fe9d8 100644 --- a/components/gitpod-protocol/src/util/gitpod-host-url.ts +++ b/components/gitpod-protocol/src/util/gitpod-host-url.ts @@ -16,10 +16,10 @@ const baseWorkspaceIDRegex = "(([a-f][0-9a-f]{7}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})|([0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8,11}))"; // this pattern matches v4 UUIDs as well as the new generated workspace ids (e.g. pink-panda-ns35kd21) -const workspaceIDRegex = RegExp(`^${baseWorkspaceIDRegex}$`); +const workspaceIDRegex = RegExp(`^(?:debug-)?${baseWorkspaceIDRegex}$`); // this pattern matches URL prefixes of workspaces -const workspaceUrlPrefixRegex = RegExp(`^([0-9]{4,6}-)?${baseWorkspaceIDRegex}\\.`); +const workspaceUrlPrefixRegex = RegExp(`^(([0-9]{4,6}|debug)-)?${baseWorkspaceIDRegex}\\.`); export namespace StartOptions { export const WORKSPACE_CLASS = "workspaceClass"; @@ -174,6 +174,10 @@ export class GitpodHostUrl { return result; } + get debugWorkspace(): boolean { + return this.url.host.match(workspaceUrlPrefixRegex)?.[2] === "debug"; + } + get workspaceId(): string | undefined { const hostSegs = this.url.host.split("."); if (hostSegs.length > 1) { @@ -181,7 +185,7 @@ export class GitpodHostUrl { if (matchResults) { // URL has a workspace prefix // port prefixes are excluded - return matchResults[0]; + return matchResults[1]; } } diff --git a/components/ide/code/leeway.Dockerfile b/components/ide/code/leeway.Dockerfile index e8c1ddc1e03d84..54458b34f83a15 100644 --- a/components/ide/code/leeway.Dockerfile +++ b/components/ide/code/leeway.Dockerfile @@ -78,7 +78,10 @@ RUN yarn --cwd extensions compile \ # Check pkg/blobserve/blobserve.go, `inlineVars` method RUN cp /vscode-web/out/vs/gitpod/browser/workbench/workbench.html /vscode-web/index.html \ && cp /vscode-web/out/vs/gitpod/browser/workbench/callback.html /vscode-web/callback.html \ + # TODO: remove next line when workbench.html changes are merged to gp-code/main && sed -i -e 's#static/##g' /vscode-web/index.html \ + && sed -i -e 's#baseUrl =.*;#baseUrl = window.location.origin;#g' /vscode-web/index.html \ + && sed -i -e 's#{{WORKBENCH_WEB_BASE_URL}}#.#g' /vscode-web/index.html \ && sed -i -e "s/{{VERSION}}/$CODE_QUALITY-$CODE_COMMIT/g" /vscode-web/index.html # cli config: alises to gitpod-code diff --git a/components/proxy/conf/Caddyfile b/components/proxy/conf/Caddyfile index 1938c66c4b53d5..5f0d2520342d54 100644 --- a/components/proxy/conf/Caddyfile +++ b/components/proxy/conf/Caddyfile @@ -397,6 +397,18 @@ https://*.*.{$GITPOD_DOMAIN} { } } + # experimental debug workspace route + @debug_workspace header_regexp host Host ^debug-(?P[a-z0-9][0-9a-z\-]+).ws(?P-[a-z0-9]+)?.{$GITPOD_DOMAIN} + handle @debug_workspace { + reverse_proxy https://ws-proxy.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:9090 { + import workspace_transport + import upstream_headers + + header_up X-Gitpod-WorkspaceId {re.host.workspaceID} + header_up X-WSProxy-Host {http.request.host} + } + } + @workspace header_regexp host Host ^(?P[a-z0-9][0-9a-z\-]+).ws(?P-[a-z0-9]+)?.{$GITPOD_DOMAIN} handle @workspace { reverse_proxy https://ws-proxy.{$KUBE_NAMESPACE}.{$KUBE_DOMAIN}:9090 { diff --git a/components/supervisor/cmd/run.go b/components/supervisor/cmd/run.go index 070713ba172023..1d14bb07037651 100644 --- a/components/supervisor/cmd/run.go +++ b/components/supervisor/cmd/run.go @@ -23,7 +23,7 @@ var runCmd = &cobra.Command{ Short: "starts the supervisor", Run: func(cmd *cobra.Command, args []string) { - log.Init(ServiceName, Version, true, os.Getenv("SUPERVISOR_DEBUG_ENABLE") == "true") + log.Init(ServiceName, Version, !runOpts.RunGP, os.Getenv("SUPERVISOR_DEBUG_ENABLE") == "true") common_grpc.SetupLogging() supervisor.Version = Version supervisor.Run(supervisor.WithRunGP(runOpts.RunGP)) diff --git a/components/supervisor/frontend/src/debug.ts b/components/supervisor/frontend/src/debug.ts new file mode 100644 index 00000000000000..a3f21974dab925 --- /dev/null +++ b/components/supervisor/frontend/src/debug.ts @@ -0,0 +1,33 @@ +/** + * 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. + */ + +import { DisposableCollection } from "@gitpod/gitpod-protocol/lib/util/disposable"; +import * as IDEFrontendService from "./ide/ide-frontend-service-impl"; +import * as IDEWorker from "./ide/ide-worker"; +import * as IDEWebSocket from "./ide/ide-web-socket"; +import { SupervisorServiceClient } from "./ide/supervisor-service-client"; + +Object.assign(window, { gitpod: {} }); +IDEWorker.install(); +IDEWebSocket.install(); +IDEWebSocket.connectWorkspace(); +const ideService = IDEFrontendService.create(); +const loadingIDE = new Promise((resolve) => window.addEventListener("DOMContentLoaded", resolve, { once: true })); +const toStop = new DisposableCollection(); + +(async () => { + const supervisorServiceClient = SupervisorServiceClient.get(Promise.resolve()); + const [ideStatus] = await Promise.all([ + supervisorServiceClient.ideReady, + supervisorServiceClient.contentReady, + loadingIDE, + ]); + // TODO(desktop support) + // const isDesktopIde = ideStatus && ideStatus.desktop && ideStatus.desktop.link; + // if (!isDesktopIde) { + toStop.push(ideService.start()); + // } +})(); diff --git a/components/supervisor/frontend/src/ide/ide-metrics-service-client.ts b/components/supervisor/frontend/src/ide/ide-metrics-service-client.ts index fa82b487531374..979b4ef2048efd 100644 --- a/components/supervisor/frontend/src/ide/ide-metrics-service-client.ts +++ b/components/supervisor/frontend/src/ide/ide-metrics-service-client.ts @@ -31,7 +31,8 @@ interface ReportErrorParam { properties?: Record; } export class IDEMetricsServiceClient { - static workspaceId? = workspaceUrl.workspaceId; + static workspaceId = workspaceUrl.workspaceId; + static debugWorkspace = workspaceUrl.debugWorkspace; static gitpodServiceClient?: GitpodServiceClient; static get instanceId(): string { @@ -70,6 +71,7 @@ export class IDEMetricsServiceClient { const p = Object.assign({}, properties); p.error_name = error.name; p.error_message = error.message; + p.debug_workspace = String(this.debugWorkspace); const url = `${MetricsUrl}/reportError`; const params: ReportErrorParam = { diff --git a/components/supervisor/frontend/src/ide/supervisor-service-client.ts b/components/supervisor/frontend/src/ide/supervisor-service-client.ts index b222cac20152e1..1d27a1be4ea69d 100644 --- a/components/supervisor/frontend/src/ide/supervisor-service-client.ts +++ b/components/supervisor/frontend/src/ide/supervisor-service-client.ts @@ -14,20 +14,18 @@ import { GitpodHostUrl } from "@gitpod/gitpod-protocol/lib/util/gitpod-host-url" export class SupervisorServiceClient { private static _instance: SupervisorServiceClient | undefined; - static get(gitpodServiceClient: GitpodServiceClient): SupervisorServiceClient { + static get(gitpodAuth: Promise): SupervisorServiceClient { if (!SupervisorServiceClient._instance) { - SupervisorServiceClient._instance = new SupervisorServiceClient(gitpodServiceClient); + SupervisorServiceClient._instance = new SupervisorServiceClient(gitpodAuth); } return SupervisorServiceClient._instance; } readonly supervisorReady = this.checkReady("supervisor"); readonly ideReady = this.supervisorReady.then(() => this.checkReady("ide")); - readonly contentReady = Promise.all([this.supervisorReady, this.gitpodServiceClient.auth]).then(() => - this.checkReady("content"), - ); + readonly contentReady = Promise.all([this.supervisorReady, this.gitpodAuth]).then(() => this.checkReady("content")); - private constructor(private readonly gitpodServiceClient: GitpodServiceClient) {} + private constructor(private readonly gitpodAuth: Promise) {} private async checkReady(kind: "content" | "ide" | "supervisor", delay?: boolean): Promise { if (delay) { diff --git a/components/supervisor/frontend/src/index.ts b/components/supervisor/frontend/src/index.ts index fe6d443c38dd22..0e1cd181fce9f4 100644 --- a/components/supervisor/frontend/src/index.ts +++ b/components/supervisor/frontend/src/index.ts @@ -8,294 +8,10 @@ * should be inserted to index.html as first body script, * all other IDE scripts should go afterwards, head element should not have scripts */ -import { IDEMetricsServiceClient, MetricsName } from "./ide/ide-metrics-service-client"; -IDEMetricsServiceClient.addCounter(MetricsName.SupervisorFrontendClientTotal).catch(() => {}); -//#region supervisor frontend error capture -function isElement(obj: any): obj is Element { - return typeof obj.getAttribute === "function"; +import { workspaceUrl } from "./shared/urls"; +if (workspaceUrl.debugWorkspace) { + require("./debug"); +} else { + require("./regular"); } - -window.addEventListener("error", (event) => { - const labels: Record = {}; - let resourceSource: string | null | undefined; - if (isElement(event.target)) { - // We take a look at what is the resource that was attempted to load; - resourceSource = event.target.getAttribute("src") || event.target.getAttribute("href"); - // If the event has a `target`, it means that it wasn't a script error - if (resourceSource) { - if (resourceSource.match(new RegExp(/\/build\/ide\/code:.+\/__files__\//g))) { - // TODO(ak) reconsider how to hide knowledge of VS Code from supervisor frontend, i.e instrument amd loader instead - labels["resource"] = "vscode-web-workbench"; - } - labels["error"] = "LoadError"; - } - } - if (event.error) { - IDEMetricsServiceClient.reportError(event.error).catch(() => {}); - } else if (labels["error"] == "LoadError") { - let error = new Error("LoadError"); - IDEMetricsServiceClient.reportError(error, { - resource: labels["resource"], - url: resourceSource ?? "", - }).catch(() => {}); - } - IDEMetricsServiceClient.addCounter(MetricsName.SupervisorFrontendErrorTotal, labels).catch(() => {}); -}); -//#endregion - -require("../src/shared/index.css"); - -import { createGitpodService, WorkspaceInstancePhase } from "@gitpod/gitpod-protocol"; -import { DisposableCollection } from "@gitpod/gitpod-protocol/lib/util/disposable"; -import * as GitpodServiceClient from "./ide/gitpod-service-client"; -import * as heartBeat from "./ide/heart-beat"; -import * as IDEFrontendService from "./ide/ide-frontend-service-impl"; -import * as IDEWorker from "./ide/ide-worker"; -import * as IDEWebSocket from "./ide/ide-web-socket"; -import { SupervisorServiceClient } from "./ide/supervisor-service-client"; -import * as LoadingFrame from "./shared/loading-frame"; -import { serverUrl, startUrl } from "./shared/urls"; - -window.gitpod = { - service: createGitpodService(serverUrl.toString()), -}; -IDEWorker.install(); -IDEWebSocket.install(); -const ideService = IDEFrontendService.create(); -const pendingGitpodServiceClient = GitpodServiceClient.create(); -const loadingIDE = new Promise((resolve) => window.addEventListener("DOMContentLoaded", resolve, { once: true })); -const toStop = new DisposableCollection(); - -(async () => { - const gitpodServiceClient = await pendingGitpodServiceClient; - IDEMetricsServiceClient.loadWorkspaceInfo(gitpodServiceClient); - - document.title = gitpodServiceClient.info.workspace.description; - - if (gitpodServiceClient.info.workspace.type !== "regular") { - return; - } - - //#region ide lifecycle - function isWorkspaceInstancePhase(phase: WorkspaceInstancePhase): boolean { - return gitpodServiceClient.info.latestInstance?.status.phase === phase; - } - if (!isWorkspaceInstancePhase("running")) { - await new Promise((resolve) => { - const listener = gitpodServiceClient.onDidChangeInfo(() => { - if (isWorkspaceInstancePhase("running")) { - listener.dispose(); - resolve(); - } - }); - }); - } - const supervisorServiceClient = SupervisorServiceClient.get(gitpodServiceClient); - const [ideStatus] = await Promise.all([ - supervisorServiceClient.ideReady, - supervisorServiceClient.contentReady, - loadingIDE, - ]); - if (isWorkspaceInstancePhase("stopping") || isWorkspaceInstancePhase("stopped")) { - return; - } - toStop.pushAll([ - IDEWebSocket.connectWorkspace(), - gitpodServiceClient.onDidChangeInfo(() => { - if (isWorkspaceInstancePhase("stopping") || isWorkspaceInstancePhase("stopped")) { - toStop.dispose(); - } - }), - ]); - const isDesktopIde = ideStatus && ideStatus.desktop && ideStatus.desktop.link; - if (!isDesktopIde) { - toStop.push(ideService.start()); - } - //#endregion -})(); - -(async () => { - document.body.style.visibility = "hidden"; - const [loading, gitpodServiceClient] = await Promise.all([ - LoadingFrame.load({ gitpodService: window.gitpod.service }), - pendingGitpodServiceClient, - ]); - const sessionId = await loading.sessionId; - - if (gitpodServiceClient.info.workspace.type !== "regular") { - return; - } - - const supervisorServiceClient = SupervisorServiceClient.get(gitpodServiceClient); - - let hideDesktopIde = false; - const serverOrigin = startUrl.url.origin; - const hideDesktopIdeEventListener = (event: MessageEvent) => { - if (event.origin === serverOrigin && event.data.type == "openBrowserIde") { - window.removeEventListener("message", hideDesktopIdeEventListener); - hideDesktopIde = true; - toStop.push(ideService.start()); - } - }; - window.addEventListener("message", hideDesktopIdeEventListener, false); - toStop.push({ dispose: () => window.removeEventListener("message", hideDesktopIdeEventListener) }); - - //#region gitpod browser telemetry - // TODO(ak) get rid of it - // it is bad usage of window.postMessage - // VS Code should use Segment directly here and publish to production/staging untrusted - // supervisor frontend should not care about IDE specifics - window.addEventListener("message", async (event) => { - const type = event.data.type; - if (type === "vscode_telemetry") { - const { event: eventName, properties } = event.data; - window.gitpod.service.server.trackEvent({ - event: eventName, - properties: { - sessionId, - instanceId: gitpodServiceClient.info.latestInstance?.id, - workspaceId: gitpodServiceClient.info.workspace.id, - type: gitpodServiceClient.info.workspace.type, - ...properties, - }, - }); - } - }); - //#endregion - - type DesktopIDEStatus = { link: string; label: string; clientID?: string; kind?: String }; - let isDesktopIde: undefined | boolean = undefined; - let ideStatus: undefined | { desktop: DesktopIDEStatus } = undefined; - - //#region current-frame - let current: HTMLElement = loading.frame; - let desktopRedirected = false; - let currentInstanceId = ""; - const nextFrame = () => { - const instance = gitpodServiceClient.info.latestInstance; - if (instance) { - // refresh web page when instanceId changed - if (currentInstanceId !== "") { - if (instance.id !== currentInstanceId && instance.ideUrl !== "") { - currentInstanceId = instance.id; - window.location.href = instance.ideUrl; - } - } else { - currentInstanceId = instance.id; - } - if (instance.status.phase === "running") { - if (!hideDesktopIde) { - if (isDesktopIde == undefined) { - return loading.frame; - } - if (isDesktopIde && !!ideStatus) { - trackDesktopIDEReady(ideStatus.desktop); - loading.setState({ - desktopIdeLink: ideStatus.desktop.link, - desktopIdeLabel: ideStatus.desktop.label || "Open Desktop IDE", - desktopIdeClientID: ideStatus.desktop.clientID, - }); - if (!desktopRedirected) { - desktopRedirected = true; - loading.openDesktopLink(ideStatus.desktop.link); - } - return loading.frame; - } - } - if (ideService.state === "ready") { - return document.body; - } - } - } - return loading.frame; - }; - const updateCurrentFrame = () => { - const newCurrent = nextFrame(); - if (current === newCurrent) { - return; - } - current.style.visibility = "hidden"; - newCurrent.style.visibility = "visible"; - if (current === document.body) { - while (document.body.firstChild && document.body.firstChild !== newCurrent) { - document.body.removeChild(document.body.firstChild); - } - while (document.body.lastChild && document.body.lastChild !== newCurrent) { - document.body.removeChild(document.body.lastChild); - } - } - current = newCurrent; - }; - - const updateLoadingState = () => { - loading.setState({ - ideFrontendFailureCause: ideService.failureCause?.message, - }); - }; - const trackStatusRenderedEvent = ( - phase: string, - properties?: { - [prop: string]: any; - }, - ) => { - window.gitpod.service.server.trackEvent({ - event: "status_rendered", - properties: { - sessionId, - instanceId: gitpodServiceClient.info.latestInstance?.id, - workspaceId: gitpodServiceClient.info.workspace.id, - type: gitpodServiceClient.info.workspace.type, - phase, - ...properties, - }, - }); - }; - let trackedDesktopIDEReady = false; - const trackDesktopIDEReady = ({ clientID, kind }: DesktopIDEStatus) => { - if (trackedDesktopIDEReady) { - return; - } - trackedDesktopIDEReady = true; - trackStatusRenderedEvent("desktop-ide-ready", { clientID, kind }); - }; - const trackIDEStatusRenderedEvent = () => { - let error: string | undefined; - if (ideService.failureCause) { - error = `${ideService.failureCause.message}\n${ideService.failureCause.stack}`; - } - trackStatusRenderedEvent(`ide-${ideService.state}`, { error }); - }; - - updateCurrentFrame(); - updateLoadingState(); - trackIDEStatusRenderedEvent(); - gitpodServiceClient.onDidChangeInfo(() => updateCurrentFrame()); - ideService.onDidChange(() => { - updateLoadingState(); - updateCurrentFrame(); - trackIDEStatusRenderedEvent(); - }); - supervisorServiceClient.ideReady - .then((newIdeStatus) => { - ideStatus = newIdeStatus; - isDesktopIde = !!ideStatus && !!ideStatus.desktop && !!ideStatus.desktop.link; - updateCurrentFrame(); - }) - .catch((error) => console.error(`Unexpected error from supervisorServiceClient.ideReady: ${error}`)); - window.addEventListener("unload", () => trackStatusRenderedEvent("window-unload"), { capture: true }); - //#endregion - - //#region heart-beat - heartBeat.track(window); - const updateHeartBeat = () => { - if (gitpodServiceClient.info.latestInstance?.status.phase === "running") { - heartBeat.schedule(gitpodServiceClient.info, sessionId); - } else { - heartBeat.cancel(); - } - }; - updateHeartBeat(); - gitpodServiceClient.onDidChangeInfo(() => updateHeartBeat()); - //#endregion -})(); diff --git a/components/supervisor/frontend/src/regular.ts b/components/supervisor/frontend/src/regular.ts new file mode 100644 index 00000000000000..3769e2d0f9e243 --- /dev/null +++ b/components/supervisor/frontend/src/regular.ts @@ -0,0 +1,297 @@ +/** + * Copyright (c) 2020 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. + */ + +import { IDEMetricsServiceClient, MetricsName } from "./ide/ide-metrics-service-client"; +IDEMetricsServiceClient.addCounter(MetricsName.SupervisorFrontendClientTotal).catch(() => {}); + +//#region supervisor frontend error capture +function isElement(obj: any): obj is Element { + return typeof obj.getAttribute === "function"; +} + +window.addEventListener("error", (event) => { + const labels: Record = {}; + let resourceSource: string | null | undefined; + if (isElement(event.target)) { + // We take a look at what is the resource that was attempted to load; + resourceSource = event.target.getAttribute("src") || event.target.getAttribute("href"); + // If the event has a `target`, it means that it wasn't a script error + if (resourceSource) { + if (resourceSource.match(new RegExp(/\/build\/ide\/code:.+\/__files__\//g))) { + // TODO(ak) reconsider how to hide knowledge of VS Code from supervisor frontend, i.e instrument amd loader instead + labels["resource"] = "vscode-web-workbench"; + } + labels["error"] = "LoadError"; + } + } + if (event.error) { + IDEMetricsServiceClient.reportError(event.error).catch(() => {}); + } else if (labels["error"] == "LoadError") { + let error = new Error("LoadError"); + IDEMetricsServiceClient.reportError(error, { + resource: labels["resource"], + url: resourceSource ?? "", + }).catch(() => {}); + } + IDEMetricsServiceClient.addCounter(MetricsName.SupervisorFrontendErrorTotal, labels).catch(() => {}); +}); +//#endregion + +require("../src/shared/index.css"); + +import { createGitpodService, WorkspaceInstancePhase } from "@gitpod/gitpod-protocol"; +import { DisposableCollection } from "@gitpod/gitpod-protocol/lib/util/disposable"; +import * as GitpodServiceClient from "./ide/gitpod-service-client"; +import * as heartBeat from "./ide/heart-beat"; +import * as IDEFrontendService from "./ide/ide-frontend-service-impl"; +import * as IDEWorker from "./ide/ide-worker"; +import * as IDEWebSocket from "./ide/ide-web-socket"; +import { SupervisorServiceClient } from "./ide/supervisor-service-client"; +import * as LoadingFrame from "./shared/loading-frame"; +import { serverUrl, startUrl } from "./shared/urls"; + +window.gitpod = { + service: createGitpodService(serverUrl.toString()), +}; +IDEWorker.install(); +IDEWebSocket.install(); +const ideService = IDEFrontendService.create(); +const pendingGitpodServiceClient = GitpodServiceClient.create(); +const loadingIDE = new Promise((resolve) => window.addEventListener("DOMContentLoaded", resolve, { once: true })); +const toStop = new DisposableCollection(); + +(async () => { + const gitpodServiceClient = await pendingGitpodServiceClient; + IDEMetricsServiceClient.loadWorkspaceInfo(gitpodServiceClient); + + document.title = gitpodServiceClient.info.workspace.description; + + if (gitpodServiceClient.info.workspace.type !== "regular") { + return; + } + + //#region ide lifecycle + function isWorkspaceInstancePhase(phase: WorkspaceInstancePhase): boolean { + return gitpodServiceClient.info.latestInstance?.status.phase === phase; + } + if (!isWorkspaceInstancePhase("running")) { + await new Promise((resolve) => { + const listener = gitpodServiceClient.onDidChangeInfo(() => { + if (isWorkspaceInstancePhase("running")) { + listener.dispose(); + resolve(); + } + }); + }); + } + const supervisorServiceClient = SupervisorServiceClient.get(gitpodServiceClient.auth); + const [ideStatus] = await Promise.all([ + supervisorServiceClient.ideReady, + supervisorServiceClient.contentReady, + loadingIDE, + ]); + if (isWorkspaceInstancePhase("stopping") || isWorkspaceInstancePhase("stopped")) { + return; + } + toStop.pushAll([ + IDEWebSocket.connectWorkspace(), + gitpodServiceClient.onDidChangeInfo(() => { + if (isWorkspaceInstancePhase("stopping") || isWorkspaceInstancePhase("stopped")) { + toStop.dispose(); + } + }), + ]); + const isDesktopIde = ideStatus && ideStatus.desktop && ideStatus.desktop.link; + if (!isDesktopIde) { + toStop.push(ideService.start()); + } + //#endregion +})(); + +(async () => { + document.body.style.visibility = "hidden"; + const [loading, gitpodServiceClient] = await Promise.all([ + LoadingFrame.load({ gitpodService: window.gitpod.service }), + pendingGitpodServiceClient, + ]); + const sessionId = await loading.sessionId; + + if (gitpodServiceClient.info.workspace.type !== "regular") { + return; + } + + const supervisorServiceClient = SupervisorServiceClient.get(gitpodServiceClient.auth); + + let hideDesktopIde = false; + const serverOrigin = startUrl.url.origin; + const hideDesktopIdeEventListener = (event: MessageEvent) => { + if (event.origin === serverOrigin && event.data.type == "openBrowserIde") { + window.removeEventListener("message", hideDesktopIdeEventListener); + hideDesktopIde = true; + toStop.push(ideService.start()); + } + }; + window.addEventListener("message", hideDesktopIdeEventListener, false); + toStop.push({ dispose: () => window.removeEventListener("message", hideDesktopIdeEventListener) }); + + //#region gitpod browser telemetry + // TODO(ak) get rid of it + // it is bad usage of window.postMessage + // VS Code should use Segment directly here and publish to production/staging untrusted + // supervisor frontend should not care about IDE specifics + window.addEventListener("message", async (event) => { + const type = event.data.type; + if (type === "vscode_telemetry") { + const { event: eventName, properties } = event.data; + window.gitpod.service.server.trackEvent({ + event: eventName, + properties: { + sessionId, + instanceId: gitpodServiceClient.info.latestInstance?.id, + workspaceId: gitpodServiceClient.info.workspace.id, + type: gitpodServiceClient.info.workspace.type, + ...properties, + }, + }); + } + }); + //#endregion + + type DesktopIDEStatus = { link: string; label: string; clientID?: string; kind?: String }; + let isDesktopIde: undefined | boolean = undefined; + let ideStatus: undefined | { desktop: DesktopIDEStatus } = undefined; + + //#region current-frame + let current: HTMLElement = loading.frame; + let desktopRedirected = false; + let currentInstanceId = ""; + const nextFrame = () => { + const instance = gitpodServiceClient.info.latestInstance; + if (instance) { + // refresh web page when instanceId changed + if (currentInstanceId !== "") { + if (instance.id !== currentInstanceId && instance.ideUrl !== "") { + currentInstanceId = instance.id; + window.location.href = instance.ideUrl; + } + } else { + currentInstanceId = instance.id; + } + if (instance.status.phase === "running") { + if (!hideDesktopIde) { + if (isDesktopIde == undefined) { + return loading.frame; + } + if (isDesktopIde && !!ideStatus) { + trackDesktopIDEReady(ideStatus.desktop); + loading.setState({ + desktopIdeLink: ideStatus.desktop.link, + desktopIdeLabel: ideStatus.desktop.label || "Open Desktop IDE", + desktopIdeClientID: ideStatus.desktop.clientID, + }); + if (!desktopRedirected) { + desktopRedirected = true; + loading.openDesktopLink(ideStatus.desktop.link); + } + return loading.frame; + } + } + if (ideService.state === "ready") { + return document.body; + } + } + } + return loading.frame; + }; + const updateCurrentFrame = () => { + const newCurrent = nextFrame(); + if (current === newCurrent) { + return; + } + current.style.visibility = "hidden"; + newCurrent.style.visibility = "visible"; + if (current === document.body) { + while (document.body.firstChild && document.body.firstChild !== newCurrent) { + document.body.removeChild(document.body.firstChild); + } + while (document.body.lastChild && document.body.lastChild !== newCurrent) { + document.body.removeChild(document.body.lastChild); + } + } + current = newCurrent; + }; + + const updateLoadingState = () => { + loading.setState({ + ideFrontendFailureCause: ideService.failureCause?.message, + }); + }; + const trackStatusRenderedEvent = ( + phase: string, + properties?: { + [prop: string]: any; + }, + ) => { + window.gitpod.service.server.trackEvent({ + event: "status_rendered", + properties: { + sessionId, + instanceId: gitpodServiceClient.info.latestInstance?.id, + workspaceId: gitpodServiceClient.info.workspace.id, + type: gitpodServiceClient.info.workspace.type, + phase, + ...properties, + }, + }); + }; + let trackedDesktopIDEReady = false; + const trackDesktopIDEReady = ({ clientID, kind }: DesktopIDEStatus) => { + if (trackedDesktopIDEReady) { + return; + } + trackedDesktopIDEReady = true; + trackStatusRenderedEvent("desktop-ide-ready", { clientID, kind }); + }; + const trackIDEStatusRenderedEvent = () => { + let error: string | undefined; + if (ideService.failureCause) { + error = `${ideService.failureCause.message}\n${ideService.failureCause.stack}`; + } + trackStatusRenderedEvent(`ide-${ideService.state}`, { error }); + }; + + updateCurrentFrame(); + updateLoadingState(); + trackIDEStatusRenderedEvent(); + gitpodServiceClient.onDidChangeInfo(() => updateCurrentFrame()); + ideService.onDidChange(() => { + updateLoadingState(); + updateCurrentFrame(); + trackIDEStatusRenderedEvent(); + }); + supervisorServiceClient.ideReady + .then((newIdeStatus) => { + ideStatus = newIdeStatus; + isDesktopIde = !!ideStatus && !!ideStatus.desktop && !!ideStatus.desktop.link; + updateCurrentFrame(); + }) + .catch((error) => console.error(`Unexpected error from supervisorServiceClient.ideReady: ${error}`)); + window.addEventListener("unload", () => trackStatusRenderedEvent("window-unload"), { capture: true }); + //#endregion + + //#region heart-beat + heartBeat.track(window); + const updateHeartBeat = () => { + if (gitpodServiceClient.info.latestInstance?.status.phase === "running") { + heartBeat.schedule(gitpodServiceClient.info, sessionId); + } else { + heartBeat.cancel(); + } + }; + updateHeartBeat(); + gitpodServiceClient.onDidChangeInfo(() => updateHeartBeat()); + //#endregion +})(); diff --git a/components/supervisor/go.mod b/components/supervisor/go.mod index 875b70e1345ef3..a89c68b976da08 100644 --- a/components/supervisor/go.mod +++ b/components/supervisor/go.mod @@ -50,6 +50,8 @@ require ( cloud.google.com/go/compute v1.7.0 // indirect cloud.google.com/go/iam v0.3.0 // indirect cloud.google.com/go/storage v1.27.0 // indirect + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/adamthesax/grpc-proxy v0.0.0-20220525203857-13e92d14f87a // indirect github.com/aws/aws-sdk-go-v2 v1.17.1 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 // indirect github.com/aws/aws-sdk-go-v2/config v1.18.3 // indirect @@ -102,6 +104,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mwitkow/grpc-proxy v0.0.0-20220126150247-db34e7bfee32 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect @@ -119,6 +122,7 @@ require ( github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect go.opencensus.io v0.23.0 // indirect go.uber.org/atomic v1.4.0 // indirect + golang.org/x/mod v0.4.2 // indirect golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect @@ -129,6 +133,7 @@ require ( gopkg.in/ini.v1 v1.57.0 // indirect gopkg.in/segmentio/analytics-go.v3 v3.1.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + honnef.co/go/tools v0.1.3 // indirect nhooyr.io/websocket v1.8.7 // indirect ) diff --git a/components/supervisor/go.sum b/components/supervisor/go.sum index 5a66452f26d0aa..3f96bd387d5367 100644 --- a/components/supervisor/go.sum +++ b/components/supervisor/go.sum @@ -61,12 +61,15 @@ cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq cloud.google.com/go/storage v1.27.0 h1:YOO045NZI9RKfCj1c5A/ZtuuENUc8OAW+gHdGnDgyMQ= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/HdrHistogram/hdrhistogram-go v1.1.0 h1:6dpdDPTRoo78HxAJ6T1HfMiKSnqhgRRqzCuPshRkQ7I= github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d h1:wvStE9wLpws31NiWUx+38wny1msZ/tm+eL5xmm4Y7So= github.com/Netflix/go-env v0.0.0-20220526054621-78278af1949d/go.mod h1:9XMFaCeRyW7fC9XJOWQ+NdAv8VLG7ys7l3x4ozEGLUQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/adamthesax/grpc-proxy v0.0.0-20220525203857-13e92d14f87a h1:8fjfNnk9RLn3F4R4XEljSOZARy1+h1f0KTh6xGFefjw= +github.com/adamthesax/grpc-proxy v0.0.0-20220525203857-13e92d14f87a/go.mod h1:Aku9EjGILrB1V88F+yfJ8CaIVaKqDeWkW2vkCbY2WSA= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -393,6 +396,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/grpc-proxy v0.0.0-20220126150247-db34e7bfee32 h1:CC9KzU7WPrK6DTppkUGiwmttoHCNwOLT7Z+stp1eIpU= +github.com/mwitkow/grpc-proxy v0.0.0-20220126150247-db34e7bfee32/go.mod h1:MvMXoufZAtqExNexqi4cjrNYE9MefKddKylxjS+//n0= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= @@ -584,6 +589,7 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -678,6 +684,7 @@ golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -875,6 +882,7 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= @@ -996,6 +1004,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/components/supervisor/hot-swap.sh b/components/supervisor/hot-swap.sh index 1040c03927bfbd..fbbf7737b8c6e1 100755 --- a/components/supervisor/hot-swap.sh +++ b/components/supervisor/hot-swap.sh @@ -37,6 +37,7 @@ echo "$component built" # upload uploadDest="/.supervisor/$component" echo "Upload Dest: $uploadDest" -ssh -F "$sshConfig" "$workspaceId" "sudo chmod 777 $uploadDest && sudo rm $uploadDest" +ssh -F "$sshConfig" "$workspaceId" "sudo chown -R gitpod:gitpod /.supervisor && rm $uploadDest 2> /dev/null" +echo "Permissions granted" scp -F "$sshConfig" -r "./supervisor" "$workspaceId":"$uploadDest" echo "Swap complete" diff --git a/components/supervisor/pkg/supervisor/config.go b/components/supervisor/pkg/supervisor/config.go index a1a26000527014..0c9149dd5b12f4 100644 --- a/components/supervisor/pkg/supervisor/config.go +++ b/components/supervisor/pkg/supervisor/config.go @@ -83,6 +83,9 @@ type StaticConfig struct { // APIEndpointPort is the port where to serve the API endpoint on APIEndpointPort int `json:"apiEndpointPort"` + // HostAPIEndpointPort is the port where to the host API endpoint served + HostAPIEndpointPort *int `json:"hostAPIEndpointPort,omitempty"` + // SSHPort is the port we run the SSH server on SSHPort int `json:"sshPort"` } diff --git a/components/supervisor/pkg/supervisor/supervisor.go b/components/supervisor/pkg/supervisor/supervisor.go index 65ccb70b0c0478..d0286f3c4315de 100644 --- a/components/supervisor/pkg/supervisor/supervisor.go +++ b/components/supervisor/pkg/supervisor/supervisor.go @@ -31,6 +31,7 @@ import ( "syscall" "time" + grpc_proxy "github.com/adamthesax/grpc-proxy/proxy" "github.com/gorilla/websocket" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus" @@ -42,7 +43,10 @@ import ( "golang.org/x/crypto/ssh" "golang.org/x/xerrors" "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/metadata" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/emptypb" "github.com/gitpod-io/gitpod/common-go/analytics" "github.com/gitpod-io/gitpod/common-go/log" @@ -181,7 +185,7 @@ func Run(options ...RunOption) { // BEWARE: we can only call buildChildProcEnv once, because it might download env vars from a one-time-secret // URL, which would fail if we tried another time. - childProcEnvvars := buildChildProcEnv(cfg, nil) + childProcEnvvars := buildChildProcEnv(cfg, nil, opts.RunGP) err = AddGitpodUserIfNotExists() if err != nil { @@ -377,12 +381,10 @@ func Run(options ...RunOption) { } wg.Add(1) - go startAPIEndpoint(ctx, cfg, &wg, apiServices, tunneledPortsService, metricsReporter, apiEndpointOpts...) + go startAPIEndpoint(ctx, cfg, &wg, apiServices, tunneledPortsService, metricsReporter, opts.RunGP, apiEndpointOpts...) - if !opts.RunGP { - wg.Add(1) - go startSSHServer(ctx, cfg, &wg, childProcEnvvars) - } + wg.Add(1) + go startSSHServer(ctx, cfg, &wg, childProcEnvvars) wg.Add(1) tasksSuccessChan := make(chan taskSuccess, 1) @@ -925,7 +927,7 @@ func prepareIDELaunch(cfg *Config, ideConfig *IDEConfig, childProcEnvvars []stri // of envvars. If envvars is nil, os.Environ() is used. // // Beware: if config contains an OTS URL the results may differ on subsequent calls. -func buildChildProcEnv(cfg *Config, envvars []string) []string { +func buildChildProcEnv(cfg *Config, envvars []string, runGP bool) []string { if envvars == nil { envvars = os.Environ() } @@ -945,6 +947,7 @@ func buildChildProcEnv(cfg *Config, envvars []string) []string { envs[nme] = val } + envs["SUPERVISOR_ADDR"] = fmt.Sprintf("localhost:%d", cfg.APIEndpointPort) if cfg.EnvvarOTS != "" { @@ -1130,7 +1133,7 @@ func isBlacklistedEnvvar(name string) bool { return false } -func startAPIEndpoint(ctx context.Context, cfg *Config, wg *sync.WaitGroup, services []RegisterableService, tunneled *ports.TunneledPortsService, metricsReporter *metrics.GrpcMetricsReporter, opts ...grpc.ServerOption) { +func startAPIEndpoint(ctx context.Context, cfg *Config, wg *sync.WaitGroup, services []RegisterableService, tunneled *ports.TunneledPortsService, metricsReporter *metrics.GrpcMetricsReporter, runGP bool, opts ...grpc.ServerOption) { defer wg.Done() defer log.Debug("startAPIEndpoint shutdown") @@ -1141,6 +1144,36 @@ func startAPIEndpoint(ctx context.Context, cfg *Config, wg *sync.WaitGroup, serv var unaryInterceptors []grpc.UnaryServerInterceptor var streamInterceptors []grpc.StreamServerInterceptor + if cfg.HostAPIEndpointPort != nil { + url := fmt.Sprintf("localhost:%d", *cfg.HostAPIEndpointPort) + conn, err := grpc.DialContext(ctx, url, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.WithError(err).Fatal("cannot access host supervisor") + } + noProxy := func(fullMethod string) bool { + return strings.Contains(fullMethod, "TasksStatus") || + strings.Contains(fullMethod, "TerminalService") || + strings.Contains(fullMethod, "InfoService") || + strings.Contains(fullMethod, "CreateSSHKeyPair") + } + unaryInterceptors = append(unaryInterceptors, func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { + if noProxy(info.FullMethod) { + return handler(ctx, req) + } + md, _ := metadata.FromIncomingContext(ctx) + resp := &emptypb.Empty{} + respErr := conn.Invoke(metadata.NewOutgoingContext(ctx, md.Copy()), info.FullMethod, req, resp) + return resp, respErr + }) + streamProxy := grpc_proxy.TransparentHandler(grpc_proxy.DefaultDirector(conn)) + streamInterceptors = append(streamInterceptors, func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { + if noProxy(info.FullMethod) { + return handler(srv, ss) + } + return streamProxy(srv, ss) + }) + } + if cfg.DebugEnable { unaryInterceptors = append(unaryInterceptors, grpc_logrus.UnaryServerInterceptor(log.Log)) streamInterceptors = append(streamInterceptors, grpc_logrus.StreamServerInterceptor(log.Log)) @@ -1155,11 +1188,6 @@ func startAPIEndpoint(ctx context.Context, cfg *Config, wg *sync.WaitGroup, serv unaryInterceptors = append(unaryInterceptors, grpcMetrics.UnaryServerInterceptor()) streamInterceptors = append(streamInterceptors, grpcMetrics.StreamServerInterceptor()) - opts = append(opts, - grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(unaryInterceptors...)), - grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(streamInterceptors...)), - ) - err = metricsReporter.Registry.Register(grpcMetrics) if err != nil { log.WithError(err).Error("supervisor: failed to register grpc metrics") @@ -1168,6 +1196,11 @@ func startAPIEndpoint(ctx context.Context, cfg *Config, wg *sync.WaitGroup, serv } } + opts = append(opts, + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(unaryInterceptors...)), + grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(streamInterceptors...)), + ) + m := cmux.New(l) restMux := grpcruntime.NewServeMux() grpcMux := m.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc")) @@ -1211,14 +1244,27 @@ func startAPIEndpoint(ctx context.Context, cfg *Config, wg *sync.WaitGroup, serv routes.Handle("/", httputil.NewSingleHostReverseProxy(ideURL)) routes.Handle("/_supervisor/frontend/", http.StripPrefix("/_supervisor/frontend", http.FileServer(http.Dir(cfg.StaticConfig.FrontendLocation)))) - routes.Handle("/_supervisor/v1/", http.StripPrefix("/_supervisor", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if strings.Contains(r.Header.Get("Content-Type"), "application/grpc") || - websocket.IsWebSocketUpgrade(r) { - http.StripPrefix("/v1", grpcWebServer).ServeHTTP(w, r) + var hostProxy *httputil.ReverseProxy + if runGP && cfg.HostAPIEndpointPort != nil { + hostProxy = httputil.NewSingleHostReverseProxy(&url.URL{ + Scheme: "http", + Host: fmt.Sprintf("localhost:%d", *cfg.HostAPIEndpointPort), + }) + } + routes.Handle("/_supervisor/v1/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.Contains(r.Header.Get("Content-Type"), "application/grpc") { + websocket.IsWebSocketUpgrade(r) + http.StripPrefix("/_supervisor/v1", grpcWebServer).ServeHTTP(w, r) + } else if hostProxy == nil || + strings.HasPrefix(r.URL.Path, "/_supervisor/v1/terminal") || + strings.HasPrefix(r.URL.Path, "/_supervisor/v1/info/workspace") || + strings.HasPrefix(r.URL.Path, "/_supervisor/v1/status/tasks") { + http.StripPrefix("/_supervisor", restMux).ServeHTTP(w, r) } else { - restMux.ServeHTTP(w, r) + hostProxy.ServeHTTP(w, r) } - }))) + })) + upgrader := websocket.Upgrader{} routes.Handle("/_supervisor/tunnel", http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { wsConn, err := upgrader.Upgrade(rw, r, nil) diff --git a/components/supervisor/pkg/supervisor/supervisor_test.go b/components/supervisor/pkg/supervisor/supervisor_test.go index 16127c7342a1cb..911ee832b70f59 100644 --- a/components/supervisor/pkg/supervisor/supervisor_test.go +++ b/components/supervisor/pkg/supervisor/supervisor_test.go @@ -122,7 +122,7 @@ func TestBuildChildProcEnv(t *testing.T) { cfg.EnvvarOTS = srv.URL } - act := buildChildProcEnv(cfg, test.Input) + act := buildChildProcEnv(cfg, test.Input, false) assert(t, act) }) } diff --git a/components/ws-proxy/pkg/proxy/config.go b/components/ws-proxy/pkg/proxy/config.go index 91ae3ae6002637..98f3a916e40ae9 100644 --- a/components/ws-proxy/pkg/proxy/config.go +++ b/components/ws-proxy/pkg/proxy/config.go @@ -71,8 +71,10 @@ func (c *HostBasedIngressConfig) Validate() error { // WorkspacePodConfig contains config around the workspace pod. type WorkspacePodConfig struct { - TheiaPort uint16 `json:"theiaPort"` - SupervisorPort uint16 `json:"supervisorPort"` + TheiaPort uint16 `json:"theiaPort"` + IDEDebugPort uint16 `json:"ideDebugPort"` + SupervisorPort uint16 `json:"supervisorPort"` + SupervisorDebugPort uint16 `json:"supervisorDebugPort"` // SupervisorImage is deprecated SupervisorImage string `json:"supervisorImage"` } @@ -85,7 +87,9 @@ func (c *WorkspacePodConfig) Validate() error { err := validation.ValidateStruct(c, validation.Field(&c.TheiaPort, validation.Required), + validation.Field(&c.IDEDebugPort, validation.Required), validation.Field(&c.SupervisorPort, validation.Required), + validation.Field(&c.SupervisorDebugPort, validation.Required), ) if len(c.SupervisorImage) > 0 { log.Warn("config value 'workspacePodConfig.supervisorImage' is deprected, use it only to be backwards compatible") diff --git a/components/ws-proxy/pkg/proxy/infoprovider.go b/components/ws-proxy/pkg/proxy/infoprovider.go index 5a8baa6f03665c..1a16581ec76fab 100644 --- a/components/ws-proxy/pkg/proxy/infoprovider.go +++ b/components/ws-proxy/pkg/proxy/infoprovider.go @@ -37,6 +37,8 @@ type WorkspaceCoords struct { ID string // The workspace port Port string + // Debug workspace + Debug bool } // WorkspaceInfoProvider is an entity that is able to provide workspaces related information. diff --git a/components/ws-proxy/pkg/proxy/proxy.go b/components/ws-proxy/pkg/proxy/proxy.go index 489f325158f817..eb1d9b23249db7 100644 --- a/components/ws-proxy/pkg/proxy/proxy.go +++ b/components/ws-proxy/pkg/proxy/proxy.go @@ -97,13 +97,19 @@ func (p *WorkspaceProxy) Handler() (http.Handler, error) { if err != nil { return nil, err } - ideRouter, portRouter, blobserveRouter := p.WorkspaceRouter(r, p.WorkspaceInfoProvider) - installWorkspaceRoutes(ideRouter, handlerConfig, p.WorkspaceInfoProvider, p.SSHHostSigners) + ideRouter, portRouter, foreignRouter := p.WorkspaceRouter(r, p.WorkspaceInfoProvider) + err = installWorkspaceRoutes(ideRouter, handlerConfig, p.WorkspaceInfoProvider, p.SSHHostSigners) + if err != nil { + return nil, err + } err = installWorkspacePortRoutes(portRouter, handlerConfig, p.WorkspaceInfoProvider) if err != nil { return nil, err } - installBlobserveRoutes(blobserveRouter, handlerConfig, p.WorkspaceInfoProvider) + err = installForeignRoutes(foreignRouter, handlerConfig, p.WorkspaceInfoProvider) + if err != nil { + return nil, err + } return r, nil } diff --git a/components/ws-proxy/pkg/proxy/routes.go b/components/ws-proxy/pkg/proxy/routes.go index 4431b9ae5ab7d1..5476a47846f872 100644 --- a/components/ws-proxy/pkg/proxy/routes.go +++ b/components/ws-proxy/pkg/proxy/routes.go @@ -70,7 +70,7 @@ func NewRouteHandlerConfig(config *Config, opts ...RouteHandlerConfigOpt) (*Rout type RouteHandler = func(r *mux.Router, config *RouteHandlerConfig) // installWorkspaceRoutes configures routing of workspace and IDE requests. -func installWorkspaceRoutes(r *mux.Router, config *RouteHandlerConfig, ip WorkspaceInfoProvider, hostKeyList []ssh.Signer) { +func installWorkspaceRoutes(r *mux.Router, config *RouteHandlerConfig, ip WorkspaceInfoProvider, hostKeyList []ssh.Signer) error { r.Use(logHandler) // Note: the order of routes defines their priority. @@ -94,6 +94,9 @@ func installWorkspaceRoutes(r *mux.Router, config *RouteHandlerConfig, ip Worksp }) routes.HandleSupervisorFrontendRoute(faviconRouter.NewRoute()) + routes.HandleDirectSupervisorRoute(enableCompression(r).PathPrefix("/_supervisor/frontend").MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool { + return rm.Vars[debugWorkspaceIdentifier] == "true" + }), false) routes.HandleSupervisorFrontendRoute(enableCompression(r).PathPrefix("/_supervisor/frontend")) routes.HandleDirectSupervisorRoute(r.PathPrefix("/_supervisor/v1/status/supervisor"), false) @@ -112,7 +115,14 @@ func installWorkspaceRoutes(r *mux.Router, config *RouteHandlerConfig, ip Worksp h.ServeHTTP(resp, req) }) }) + err := installDebugWorkspaceRoutes(rootRouter.MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool { + return rm.Vars[debugWorkspaceIdentifier] == "true" + }).Subrouter(), routes.Config, routes.InfoProvider) + if err != nil { + return err + } routes.HandleRoot(rootRouter.NewRoute()) + return nil } func enableCompression(r *mux.Router) *mux.Router { @@ -250,7 +260,7 @@ func (ir *ideRoutes) HandleRoot(route *mux.Route) { r.Use(ir.Config.CorsHandler) r.Use(ir.workspaceMustExistHandler) - workspaceIDEPass := ir.Config.WorkspaceAuthHandler( + directIDEPass := ir.Config.WorkspaceAuthHandler( proxyPass(ir.Config, ir.InfoProvider, workspacePodResolver), ) // always hit the blobserver to ensure that blob is downloaded @@ -315,7 +325,31 @@ func (ir *ideRoutes) HandleRoot(route *mux.Route) { return image }, } - }, withHTTPErrorHandler(workspaceIDEPass), withUseTargetHost())) + }, withHTTPErrorHandler(directIDEPass), withUseTargetHost())) +} + +func installForeignRoutes(r *mux.Router, config *RouteHandlerConfig, infoProvider WorkspaceInfoProvider) error { + installWorkspacePortRoutes(r.MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool { + workspacePathPrefix := rm.Vars[workspacePathPrefixIdentifier] + if workspacePathPrefix == "" || rm.Vars[workspacePortIdentifier] == "" { + return false + } + r.URL.Path = strings.TrimPrefix(r.URL.Path, workspacePathPrefix) + return true + }).Subrouter(), config, infoProvider) + err := installDebugWorkspaceRoutes(r.MatcherFunc(func(r *http.Request, rm *mux.RouteMatch) bool { + workspacePathPrefix := rm.Vars[workspacePathPrefixIdentifier] + if workspacePathPrefix == "" || rm.Vars[debugWorkspaceIdentifier] != "true" { + return false + } + r.URL.Path = strings.TrimPrefix(r.URL.Path, workspacePathPrefix) + return true + }).Subrouter(), config, infoProvider) + if err != nil { + return err + } + installBlobserveRoutes(r.NewRoute().Subrouter(), config, infoProvider) + return nil } const imagePathSeparator = "/__files__" @@ -343,6 +377,22 @@ func installBlobserveRoutes(r *mux.Router, config *RouteHandlerConfig, infoProvi r.NewRoute().Handler(proxyPass(config, infoProvider, targetResolver, withLongTermCaching(), withUseTargetHost())) } +// installDebugWorkspaceRoutes configures for debug workspace. +func installDebugWorkspaceRoutes(r *mux.Router, config *RouteHandlerConfig, infoProvider WorkspaceInfoProvider) error { + showPortNotFoundPage, err := servePortNotFoundPage(config.Config) + if err != nil { + return err + } + + r.Use(logHandler) + r.Use(config.CorsHandler) + r.Use(config.WorkspaceAuthHandler) + // filter all session cookies + r.Use(sensitiveCookieHandler(config.Config.GitpodInstallation.HostName)) + r.NewRoute().HandlerFunc(proxyPass(config, infoProvider, workspacePodResolver, withHTTPErrorHandler(showPortNotFoundPage))) + return nil +} + // installWorkspacePortRoutes configures routing for exposed ports. func installWorkspacePortRoutes(r *mux.Router, config *RouteHandlerConfig, infoProvider WorkspaceInfoProvider) error { showPortNotFoundPage, err := servePortNotFoundPage(config.Config) @@ -385,8 +435,14 @@ func installWorkspacePortRoutes(r *mux.Router, config *RouteHandlerConfig, infoP // workspacePodResolver resolves to the workspace pod's url from the given request. func workspacePodResolver(config *Config, infoProvider WorkspaceInfoProvider, req *http.Request) (url *url.URL, err error) { coords := getWorkspaceCoords(req) + var port string + if coords.Debug { + port = fmt.Sprint(config.WorkspacePodConfig.IDEDebugPort) + } else { + port = fmt.Sprint(config.WorkspacePodConfig.TheiaPort) + } workspaceInfo := infoProvider.WorkspaceInfo(coords.ID) - return buildWorkspacePodURL(workspaceInfo.IPAddress, fmt.Sprint(config.WorkspacePodConfig.TheiaPort)) + return buildWorkspacePodURL(workspaceInfo.IPAddress, port) } // workspacePodPortResolver resolves to the workspace pods ports. @@ -399,8 +455,14 @@ func workspacePodPortResolver(config *Config, infoProvider WorkspaceInfoProvider // workspacePodSupervisorResolver resolves to the workspace pods Supervisor url from the given request. func workspacePodSupervisorResolver(config *Config, infoProvider WorkspaceInfoProvider, req *http.Request) (url *url.URL, err error) { coords := getWorkspaceCoords(req) + var port string + if coords.Debug { + port = fmt.Sprint(config.WorkspacePodConfig.SupervisorDebugPort) + } else { + port = fmt.Sprint(config.WorkspacePodConfig.SupervisorPort) + } workspaceInfo := infoProvider.WorkspaceInfo(coords.ID) - return buildWorkspacePodURL(workspaceInfo.IPAddress, fmt.Sprint(config.WorkspacePodConfig.SupervisorPort)) + return buildWorkspacePodURL(workspaceInfo.IPAddress, port) } func dynamicIDEResolver(config *Config, infoProvider WorkspaceInfoProvider, req *http.Request) (res *url.URL, err error) { diff --git a/components/ws-proxy/pkg/proxy/routes_test.go b/components/ws-proxy/pkg/proxy/routes_test.go index 9e30f2b6ad0293..45dcc2e22ff1d1 100644 --- a/components/ws-proxy/pkg/proxy/routes_test.go +++ b/components/ws-proxy/pkg/proxy/routes_test.go @@ -37,7 +37,8 @@ const ( ) var ( - workspaces = []WorkspaceInfo{ + debugWorkspaceURL = "https://debug-amaranth-smelt-9ba20cc1.test-domain.com/" + workspaces = []WorkspaceInfo{ { IDEImage: "gitpod-io/ide:latest", SupervisorImage: "gitpod-io/supervisor:latest", @@ -55,12 +56,14 @@ var ( }, } - ideServerHost = "localhost:20000" - workspacePort = uint16(20001) - supervisorPort = uint16(20002) - workspaceHost = fmt.Sprintf("localhost:%d", workspacePort) - portServeHost = fmt.Sprintf("localhost:%d", workspaces[0].Ports[0].Port) - blobServeHost = "localhost:20003" + ideServerHost = "localhost:20000" + workspacePort = uint16(20001) + supervisorPort = uint16(20002) + workspaceDebugPort = uint16(20004) + supervisorDebugPort = uint16(20005) + workspaceHost = fmt.Sprintf("localhost:%d", workspacePort) + portServeHost = fmt.Sprintf("localhost:%d", workspaces[0].Ports[0].Port) + blobServeHost = "localhost:20003" config = Config{ TransportConfig: &TransportConfig{ @@ -79,8 +82,10 @@ var ( Scheme: "http", }, WorkspacePodConfig: &WorkspacePodConfig{ - TheiaPort: workspacePort, - SupervisorPort: supervisorPort, + TheiaPort: workspacePort, + SupervisorPort: supervisorPort, + IDEDebugPort: workspaceDebugPort, + SupervisorDebugPort: supervisorDebugPort, }, BuiltinPages: BuiltinPagesConfig{ Location: "../../public", @@ -198,11 +203,13 @@ func TestRoutes(t *testing.T) { Body string } type Targets struct { - IDE *Target - Blobserve *Target - Workspace *Target - Supervisor *Target - Port *Target + IDE *Target + Blobserve *Target + Workspace *Target + DebugWorkspace *Target + Supervisor *Target + DebugSupervisor *Target + Port *Target } tests := []struct { Desc string @@ -351,6 +358,54 @@ func TestRoutes(t *testing.T) { Body: "blobserve hit: /image/test.html\nhost: localhost:20003\n", }, }, + { + Desc: "port foreign resource", + Config: &config, + Request: modifyRequest(httptest.NewRequest("GET", "https://v--sr1o1nu24nqdf809l0u27jk5t7"+wsHostSuffix+"/28080-amaranth-smelt-9ba20cc1/test.html", nil), + addHostHeader, + addOwnerToken(workspaces[0].InstanceID, workspaces[0].Auth.OwnerToken), + ), + Targets: &Targets{ + Port: &Target{ + Handler: func(w http.ResponseWriter, r *http.Request, requestCount uint8) { + fmt.Fprintf(w, "host: %s\n", r.Host) + fmt.Fprintf(w, "path: %s\n", r.URL.Path) + }, + }, + }, + Expectation: Expectation{ + Status: http.StatusOK, + Header: http.Header{ + "Content-Length": {"69"}, + "Content-Type": {"text/plain; charset=utf-8"}, + }, + Body: "host: v--sr1o1nu24nqdf809l0u27jk5t7.test-domain.com\npath: /test.html\n", + }, + }, + { + Desc: "debug foreign resource", + Config: &config, + Request: modifyRequest(httptest.NewRequest("GET", "https://v--sr1o1nu24nqdf809l0u27jk5t7"+wsHostSuffix+"/debug-amaranth-smelt-9ba20cc1/test.html", nil), + addHostHeader, + addOwnerToken(workspaces[0].InstanceID, workspaces[0].Auth.OwnerToken), + ), + Targets: &Targets{ + DebugWorkspace: &Target{ + Handler: func(w http.ResponseWriter, r *http.Request, requestCount uint8) { + fmt.Fprintf(w, "host: %s\n", r.Host) + fmt.Fprintf(w, "path: %s\n", r.URL.Path) + }, + }, + }, + Expectation: Expectation{ + Status: http.StatusOK, + Header: http.Header{ + "Content-Length": {"69"}, + "Content-Type": {"text/plain; charset=utf-8"}, + }, + Body: "host: v--sr1o1nu24nqdf809l0u27jk5t7.test-domain.com\npath: /test.html\n", + }, + }, { Desc: "CORS preflight", Config: &config, @@ -592,6 +647,41 @@ func TestRoutes(t *testing.T) { Body: "host: 28080-amaranth-smelt-9ba20cc1.test-domain.com\n", }, }, + { + Desc: "debug IDE authorized GE", + Config: &config, + Request: modifyRequest(httptest.NewRequest("GET", debugWorkspaceURL, nil), + addHostHeader, + addOwnerToken(workspaces[0].InstanceID, workspaces[0].Auth.OwnerToken), + ), + Targets: &Targets{DebugWorkspace: &Target{Status: http.StatusOK}}, + Expectation: Expectation{ + Status: http.StatusOK, + Header: http.Header{ + "Content-Length": {"23"}, + "Content-Type": {"text/plain; charset=utf-8"}, + "Vary": {"Accept-Encoding"}, + }, + Body: "debug workspace hit: /\n", + }, + }, + { + Desc: "debug supervisor frontend /main.js", + Config: &config, + Request: modifyRequest(httptest.NewRequest("GET", debugWorkspaceURL+"_supervisor/frontend/main.js", nil), + addHostHeader, + ), + Targets: &Targets{DebugSupervisor: &Target{Status: http.StatusOK}}, + Expectation: Expectation{ + Status: http.StatusOK, + Header: http.Header{ + "Content-Length": {"52"}, + "Content-Type": {"text/plain; charset=utf-8"}, + "Vary": {"Accept-Encoding"}, + }, + Body: "supervisor debug hit: /_supervisor/frontend/main.js\n", + }, + }, } log.Init("ws-proxy-test", "", false, true) @@ -639,7 +729,9 @@ func TestRoutes(t *testing.T) { controlTarget(test.Targets.Blobserve, "blobserve", blobServeHost, true) controlTarget(test.Targets.Port, "port", portServeHost, true) controlTarget(test.Targets.Workspace, "workspace", workspaceHost, false) + controlTarget(test.Targets.DebugWorkspace, "debug workspace", fmt.Sprintf("localhost:%d", workspaceDebugPort), false) controlTarget(test.Targets.Supervisor, "supervisor", fmt.Sprintf("localhost:%d", supervisorPort), false) + controlTarget(test.Targets.DebugSupervisor, "supervisor debug", fmt.Sprintf("localhost:%d", supervisorDebugPort), false) cfg := config if test.Config != nil { diff --git a/components/ws-proxy/pkg/proxy/workspacerouter.go b/components/ws-proxy/pkg/proxy/workspacerouter.go index 79bda52b4d1708..14ff9ccf514cad 100644 --- a/components/ws-proxy/pkg/proxy/workspacerouter.go +++ b/components/ws-proxy/pkg/proxy/workspacerouter.go @@ -28,6 +28,11 @@ const ( // This pattern matches v4 UUIDs as well as the new generated workspace ids (e.g. pink-panda-ns35kd21). workspaceIDRegex = "(?P<" + workspaceIDIdentifier + ">[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-z]{2,16}-[0-9a-z]{2,16}-[0-9a-z]{8,11})" workspacePortRegex = "(?P<" + workspacePortIdentifier + ">[0-9]+)-" + + debugWorkspaceIdentifier = "debugWorkspace" + debugWorkspaceRegex = "(?P<" + debugWorkspaceIdentifier + ">debug-)?" + + workspacePathPrefixIdentifier = "workspacePathPrefix" ) // WorkspaceRouter is a function that configures subrouters (one for theia, one for the exposed ports) on the given router @@ -56,9 +61,9 @@ func HostBasedRouter(header, wsHostSuffix string, wsHostSuffixRegex string) Work } return host } - blobserveRouter = r.MatcherFunc(matchBlobserveHostHeader(wsHostSuffix, getHostHeader)).Subrouter() - portRouter = r.MatcherFunc(matchWorkspaceHostHeader(wsHostSuffix, getHostHeader, true)).Subrouter() - ideRouter = r.MatcherFunc(matchWorkspaceHostHeader(allClusterWsHostSuffixRegex, getHostHeader, false)).Subrouter() + foreignRouter = r.MatcherFunc(matchForeignHostHeader(wsHostSuffix, getHostHeader)).Subrouter() + portRouter = r.MatcherFunc(matchWorkspaceHostHeader(wsHostSuffix, getHostHeader, true)).Subrouter() + ideRouter = r.MatcherFunc(matchWorkspaceHostHeader(allClusterWsHostSuffixRegex, getHostHeader, false)).Subrouter() ) r.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { @@ -66,16 +71,18 @@ func HostBasedRouter(header, wsHostSuffix string, wsHostSuffixRegex string) Work log.Debugf("no match for path %s, host: %s", req.URL.Path, hostname) w.WriteHeader(http.StatusNotFound) }) - return ideRouter, portRouter, blobserveRouter + return ideRouter, portRouter, foreignRouter } } type hostHeaderProvider func(req *http.Request) string func matchWorkspaceHostHeader(wsHostSuffix string, headerProvider hostHeaderProvider, matchPort bool) mux.MatcherFunc { - regexPrefix := workspaceIDRegex + var regexPrefix string if matchPort { regexPrefix = workspacePortRegex + workspaceIDRegex + } else { + regexPrefix = debugWorkspaceRegex + workspaceIDRegex } r := regexp.MustCompile("^" + regexPrefix + wsHostSuffix) @@ -86,25 +93,32 @@ func matchWorkspaceHostHeader(wsHostSuffix string, headerProvider hostHeaderProv return false } - var workspaceID, workspacePort string + var workspaceID, workspacePort, debugWorkspace string matches := r.FindStringSubmatch(hostname) + if len(matches) < 3 { + return false + } if matchPort { - if len(matches) < 3 { - return false - } // https://3000-coral-dragon-ilr0r6eq.ws-eu10.gitpod.io/index.html + // debugWorkspace: // workspaceID: coral-dragon-ilr0r6eq // workspacePort: 3000 workspaceID = matches[2] workspacePort = matches[1] } else { - if len(matches) < 2 { - return false + // https://debug-coral-dragon-ilr0r6eq.ws-eu10.gitpod.io/index.html + // debugWorkspace: true + // workspaceID: coral-dragon-ilr0r6eq + // workspacePort: + if matches[1] != "" { + debugWorkspace = "true" } + // https://coral-dragon-ilr0r6eq.ws-eu10.gitpod.io/index.html + // debugWorkspace: // workspaceID: coral-dragon-ilr0r6eq // workspacePort: - workspaceID = matches[1] + workspaceID = matches[2] } if workspaceID == "" { @@ -122,12 +136,18 @@ func matchWorkspaceHostHeader(wsHostSuffix string, headerProvider hostHeaderProv if workspacePort != "" { m.Vars[workspacePortIdentifier] = workspacePort } + if debugWorkspace != "" { + m.Vars[debugWorkspaceIdentifier] = debugWorkspace + } return true } } -func matchBlobserveHostHeader(wsHostSuffix string, headerProvider hostHeaderProvider) mux.MatcherFunc { +func matchForeignHostHeader(wsHostSuffix string, headerProvider hostHeaderProvider) mux.MatcherFunc { + pathPortRegex := regexp.MustCompile("^/" + workspacePortRegex + workspaceIDRegex + "/") + pathDebugRegex := regexp.MustCompile("^/" + debugWorkspaceRegex + workspaceIDRegex + "/") + r := regexp.MustCompile("^(?:v--)?[0-9a-v]+" + wsHostSuffix) return func(req *http.Request, m *mux.RouteMatch) bool { hostname := headerProvider(req) @@ -136,15 +156,38 @@ func matchBlobserveHostHeader(wsHostSuffix string, headerProvider hostHeaderProv } matches := r.FindStringSubmatch(hostname) - return len(matches) >= 1 + if len(matches) < 1 { + return false + } + matches = pathPortRegex.FindStringSubmatch(req.URL.Path) + if len(matches) < 3 { + matches = pathDebugRegex.FindStringSubmatch(req.URL.Path) + } + if len(matches) < 3 { + return true + } + + if m.Vars == nil { + m.Vars = make(map[string]string) + } + m.Vars[workspacePathPrefixIdentifier] = strings.TrimRight(matches[0], "/") + m.Vars[workspaceIDIdentifier] = matches[2] + if matches[1] == "debug-" { + m.Vars[debugWorkspaceIdentifier] = "true" + } else { + m.Vars[workspacePortIdentifier] = matches[1] + } + + return true } } func getWorkspaceCoords(req *http.Request) WorkspaceCoords { vars := mux.Vars(req) return WorkspaceCoords{ - ID: vars[workspaceIDIdentifier], - Port: vars[workspacePortIdentifier], + ID: vars[workspaceIDIdentifier], + Port: vars[workspacePortIdentifier], + Debug: vars[debugWorkspaceIdentifier] == "true", } } diff --git a/components/ws-proxy/pkg/proxy/workspacerouter_test.go b/components/ws-proxy/pkg/proxy/workspacerouter_test.go index d407b0555dd85d..6f2ba087945671 100644 --- a/components/ws-proxy/pkg/proxy/workspacerouter_test.go +++ b/components/ws-proxy/pkg/proxy/workspacerouter_test.go @@ -23,6 +23,7 @@ func TestWorkspaceRouter(t *testing.T) { Status int URL string AdditionalHitCount int + DebugWorkspace string } tests := []struct { Name string @@ -47,6 +48,21 @@ func TestWorkspaceRouter(t *testing.T) { URL: "http://amaranth-smelt-9ba20cc1.ws.gitpod.dev/", }, }, + { + Name: "host-based debug workspace access", + URL: "http://debug-amaranth-smelt-9ba20cc1.ws.gitpod.dev/", + Headers: map[string]string{ + forwardedHostnameHeader: "debug-amaranth-smelt-9ba20cc1.ws.gitpod.dev", + }, + Router: HostBasedRouter(forwardedHostnameHeader, wsHostSuffix, wsHostRegex), + WSHostSuffix: wsHostSuffix, + Expected: Expectation{ + DebugWorkspace: "true", + WorkspaceID: "amaranth-smelt-9ba20cc1", + Status: http.StatusOK, + URL: "http://debug-amaranth-smelt-9ba20cc1.ws.gitpod.dev/", + }, + }, { Name: "host-based port access", URL: "http://1234-amaranth-smelt-9ba20cc1.ws.gitpod.dev/", @@ -79,6 +95,7 @@ func TestWorkspaceRouter(t *testing.T) { act.WorkspaceID = vars[workspaceIDIdentifier] act.WorkspacePort = vars[workspacePortIdentifier] + act.DebugWorkspace = vars[debugWorkspaceIdentifier] act.URL = req.URL.String() act.AdditionalHitCount++ } diff --git a/components/ws-proxy/pkg/sshproxy/server.go b/components/ws-proxy/pkg/sshproxy/server.go index ce000c5ce6900d..0949d915283306 100644 --- a/components/ws-proxy/pkg/sshproxy/server.go +++ b/components/ws-proxy/pkg/sshproxy/server.go @@ -122,6 +122,11 @@ func New(signers []ssh.Signer, workspaceInfoProvider p.WorkspaceInfoProvider, he NoClientAuthCallback: func(conn ssh.ConnMetadata) (*ssh.Permissions, error) { args := strings.Split(conn.User(), "#") workspaceId := args[0] + var debugWorkspace string + if strings.HasPrefix(workspaceId, "debug-") { + debugWorkspace = "true" + workspaceId = strings.TrimPrefix(workspaceId, "debug-") + } wsInfo, err := server.GetWorkspaceInfo(workspaceId) if err != nil { return nil, err @@ -136,12 +141,18 @@ func New(signers []ssh.Signer, workspaceInfoProvider p.WorkspaceInfoProvider, he server.TrackSSHConnection(wsInfo, "auth", nil) return &ssh.Permissions{ Extensions: map[string]string{ - "workspaceId": workspaceId, + "workspaceId": workspaceId, + "debugWorkspace": debugWorkspace, }, }, nil }, PasswordCallback: func(conn ssh.ConnMetadata, password []byte) (perm *ssh.Permissions, err error) { workspaceId, ownerToken := conn.User(), string(password) + var debugWorkspace string + if strings.HasPrefix(workspaceId, "debug-") { + debugWorkspace = "true" + workspaceId = strings.TrimPrefix(workspaceId, "debug-") + } wsInfo, err := server.GetWorkspaceInfo(workspaceId) if err != nil { return nil, err @@ -154,12 +165,18 @@ func New(signers []ssh.Signer, workspaceInfoProvider p.WorkspaceInfoProvider, he } return &ssh.Permissions{ Extensions: map[string]string{ - "workspaceId": workspaceId, + "workspaceId": workspaceId, + "debugWorkspace": debugWorkspace, }, }, nil }, PublicKeyCallback: func(conn ssh.ConnMetadata, pk ssh.PublicKey) (perm *ssh.Permissions, err error) { workspaceId := conn.User() + var debugWorkspace string + if strings.HasPrefix(workspaceId, "debug-") { + debugWorkspace = "true" + workspaceId = strings.TrimPrefix(workspaceId, "debug-") + } wsInfo, err := server.GetWorkspaceInfo(workspaceId) if err != nil { return nil, err @@ -175,7 +192,8 @@ func New(signers []ssh.Signer, workspaceInfoProvider p.WorkspaceInfoProvider, he } return &ssh.Permissions{ Extensions: map[string]string{ - "workspaceId": workspaceId, + "workspaceId": workspaceId, + "debugWorkspace": debugWorkspace, }, }, nil }, @@ -225,13 +243,18 @@ func (s *Server) HandleConn(c net.Conn) { return } workspaceId := clientConn.Permissions.Extensions["workspaceId"] + debugWorkspace := clientConn.Permissions.Extensions["debugWorkspace"] == "true" wsInfo := s.workspaceInfoProvider.WorkspaceInfo(workspaceId) if wsInfo == nil { ReportSSHAttemptMetrics(ErrWorkspaceNotFound) return } ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - key, err := s.GetWorkspaceSSHKey(ctx, wsInfo.IPAddress) + supervisorPort := "22999" + if debugWorkspace { + supervisorPort = "24999" + } + key, err := s.GetWorkspaceSSHKey(ctx, wsInfo.IPAddress, supervisorPort) if err != nil { cancel() s.TrackSSHConnection(wsInfo, "connect", ErrCreateSSHKey) @@ -248,7 +271,11 @@ func (s *Server) HandleConn(c net.Conn) { OwnerUserId: wsInfo.OwnerUserId, WorkspacePrivateKey: key, } - remoteAddr := wsInfo.IPAddress + ":23001" + sshPort := "23001" + if debugWorkspace { + sshPort = "25001" + } + remoteAddr := wsInfo.IPAddress + ":" + sshPort conn, err := net.Dial("tcp", remoteAddr) if err != nil { s.TrackSSHConnection(wsInfo, "connect", ErrConnFailed) @@ -371,8 +398,8 @@ func (s *Server) VerifyPublicKey(ctx context.Context, wsInfo *p.WorkspaceInfo, p return false, nil } -func (s *Server) GetWorkspaceSSHKey(ctx context.Context, workspaceIP string) (ssh.Signer, error) { - supervisorConn, err := grpc.Dial(workspaceIP+":22999", grpc.WithTransportCredentials(insecure.NewCredentials())) +func (s *Server) GetWorkspaceSSHKey(ctx context.Context, workspaceIP string, supervisorPort string) (ssh.Signer, error) { + supervisorConn, err := grpc.Dial(workspaceIP+":"+supervisorPort, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, xerrors.Errorf("failed connecting to supervisor: %w", err) } diff --git a/install/installer/cmd/testdata/render/aws-setup/output.golden b/install/installer/cmd/testdata/render/aws-setup/output.golden index 558f7af7040826..a21638f3bed2fb 100644 --- a/install/installer/cmd/testdata/render/aws-setup/output.golden +++ b/install/installer/cmd/testdata/render/aws-setup/output.golden @@ -1241,6 +1241,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -3481,7 +3485,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5117,7 +5121,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5699,7 +5703,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -8406,7 +8412,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: a46080fdf584e8531ec566bcd7761b7ceca365777f0859476a0a9c68beb99737 + gitpod.io/checksum_config: 4cf8c1dc1a742a6532052408497a232b8ca6486d3038d9c0b77f90cb126ae968 creationTimestamp: null labels: app: gitpod @@ -10435,7 +10441,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: f88055f3ce58c8eca0064866deeb5131473b6ba2980dce8bf4f9989e997f1ff5 + gitpod.io/checksum_config: 714740065966c437ee71b41e8953aa6bb77ac83596e2326b267a2b954f60242d creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/azure-setup/output.golden b/install/installer/cmd/testdata/render/azure-setup/output.golden index 9d5eab5d2c1305..d1c88b87f3b848 100644 --- a/install/installer/cmd/testdata/render/azure-setup/output.golden +++ b/install/installer/cmd/testdata/render/azure-setup/output.golden @@ -1264,6 +1264,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -3485,7 +3489,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5101,7 +5105,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5675,7 +5679,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -8363,7 +8369,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: a46080fdf584e8531ec566bcd7761b7ceca365777f0859476a0a9c68beb99737 + gitpod.io/checksum_config: 4cf8c1dc1a742a6532052408497a232b8ca6486d3038d9c0b77f90cb126ae968 creationTimestamp: null labels: app: gitpod @@ -10504,7 +10510,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: f88055f3ce58c8eca0064866deeb5131473b6ba2980dce8bf4f9989e997f1ff5 + gitpod.io/checksum_config: 714740065966c437ee71b41e8953aa6bb77ac83596e2326b267a2b954f60242d creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/custom-pull-repository/output.golden b/install/installer/cmd/testdata/render/custom-pull-repository/output.golden index 18645fe4bb43f6..08b7a1c1657c57 100644 --- a/install/installer/cmd/testdata/render/custom-pull-repository/output.golden +++ b/install/installer/cmd/testdata/render/custom-pull-repository/output.golden @@ -1366,6 +1366,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -3792,7 +3796,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "registry.mydomain.com/namespace/supervisor:test", "ideOptions": { "options": { @@ -5508,7 +5512,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "registry.mydomain.com/namespace/supervisor:test", "ideOptions": { "options": { @@ -6082,7 +6086,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "registry.mydomain.com/namespace/supervisor:test" }, "builtinPages": { @@ -9083,7 +9089,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: ca8305ae7d44552aace863e8af17bbdef1c57b0259ee7bdc20a7ab9061d2fb9f + gitpod.io/checksum_config: 46bf85a0c156df9b29d1750baba3e50c29eb608aa27cea65c04286a059bb283c creationTimestamp: null labels: app: gitpod @@ -11308,7 +11314,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 54a20d5d027f3637c14e46ec387021c29056fc293a700bd87f470fdd7d796937 + gitpod.io/checksum_config: f0a077b5cee652ccf128621f7f7cc23ae4cdb4225b4aa32942eccaa2be4e528e creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/customization/output.golden b/install/installer/cmd/testdata/render/customization/output.golden index d160ff33b9a8d3..54d8799069e1d8 100644 --- a/install/installer/cmd/testdata/render/customization/output.golden +++ b/install/installer/cmd/testdata/render/customization/output.golden @@ -1491,6 +1491,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -4350,7 +4354,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -6111,7 +6115,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -6705,7 +6709,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -9840,7 +9846,7 @@ spec: metadata: annotations: gitpod.io: hello - gitpod.io/checksum_config: e74e2e6cf0f4335d2ee5420aa16b09783844ae8c9c8f109ac086d2f10634fb7c + gitpod.io/checksum_config: 4db8afb1e5ecbc5a450dcd2baba97cdebe599b93424941f890ebe47494f9abf3 hello: world creationTimestamp: null labels: @@ -12189,7 +12195,7 @@ spec: metadata: annotations: gitpod.io: hello - gitpod.io/checksum_config: 7cb3858d6b6cd2fbb0e4ad1dc049ff23fca40a07e41db9c74a4b6b4ecf9ac39a + gitpod.io/checksum_config: be61210a63646185a45ed41810fa0ff56fb433059593b7eaea7a23d9a3cc4bdf hello: world creationTimestamp: null labels: diff --git a/install/installer/cmd/testdata/render/external-registry/output.golden b/install/installer/cmd/testdata/render/external-registry/output.golden index d39bcf008115ef..ab6bce20a8c9dc 100644 --- a/install/installer/cmd/testdata/render/external-registry/output.golden +++ b/install/installer/cmd/testdata/render/external-registry/output.golden @@ -1295,6 +1295,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -3626,7 +3630,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5288,7 +5292,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5862,7 +5866,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -8803,7 +8809,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: a46080fdf584e8531ec566bcd7761b7ceca365777f0859476a0a9c68beb99737 + gitpod.io/checksum_config: 4cf8c1dc1a742a6532052408497a232b8ca6486d3038d9c0b77f90cb126ae968 creationTimestamp: null labels: app: gitpod @@ -10931,7 +10937,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: f88055f3ce58c8eca0064866deeb5131473b6ba2980dce8bf4f9989e997f1ff5 + gitpod.io/checksum_config: 714740065966c437ee71b41e8953aa6bb77ac83596e2326b267a2b954f60242d creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/gcp-setup/output.golden b/install/installer/cmd/testdata/render/gcp-setup/output.golden index 29277d43633b51..4c04381952e7b8 100644 --- a/install/installer/cmd/testdata/render/gcp-setup/output.golden +++ b/install/installer/cmd/testdata/render/gcp-setup/output.golden @@ -1241,6 +1241,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -3456,7 +3460,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5062,7 +5066,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5634,7 +5638,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -8343,7 +8349,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: a46080fdf584e8531ec566bcd7761b7ceca365777f0859476a0a9c68beb99737 + gitpod.io/checksum_config: 4cf8c1dc1a742a6532052408497a232b8ca6486d3038d9c0b77f90cb126ae968 creationTimestamp: null labels: app: gitpod @@ -10394,7 +10400,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: f88055f3ce58c8eca0064866deeb5131473b6ba2980dce8bf4f9989e997f1ff5 + gitpod.io/checksum_config: 714740065966c437ee71b41e8953aa6bb77ac83596e2326b267a2b954f60242d creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/http-proxy/output.golden b/install/installer/cmd/testdata/render/http-proxy/output.golden index b83c6e71d74d7c..dbf609fc96af57 100644 --- a/install/installer/cmd/testdata/render/http-proxy/output.golden +++ b/install/installer/cmd/testdata/render/http-proxy/output.golden @@ -1366,6 +1366,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -3795,7 +3799,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5511,7 +5515,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -6085,7 +6089,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -9568,7 +9574,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 0d9d1c68723466a253996f0a96bf0682ce57b89ebcb4ae1e4a643dd788ef1cab + gitpod.io/checksum_config: 4ef10d5efbf848826fd32a67c8ae990b45cdbab8abf649101c143908aceee122 creationTimestamp: null labels: app: gitpod @@ -12996,7 +13002,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: f88055f3ce58c8eca0064866deeb5131473b6ba2980dce8bf4f9989e997f1ff5 + gitpod.io/checksum_config: 714740065966c437ee71b41e8953aa6bb77ac83596e2326b267a2b954f60242d creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/kind-ide/output.golden b/install/installer/cmd/testdata/render/kind-ide/output.golden index 1d4d3fe000cb9b..9713de553df708 100644 --- a/install/installer/cmd/testdata/render/kind-ide/output.golden +++ b/install/installer/cmd/testdata/render/kind-ide/output.golden @@ -385,6 +385,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -1263,7 +1267,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -2585,7 +2589,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -3410,7 +3414,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 0d9d1c68723466a253996f0a96bf0682ce57b89ebcb4ae1e4a643dd788ef1cab + gitpod.io/checksum_config: 4ef10d5efbf848826fd32a67c8ae990b45cdbab8abf649101c143908aceee122 creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/kind-meta/output.golden b/install/installer/cmd/testdata/render/kind-meta/output.golden index c8a3fc3d7a251c..e4c27a8ccd4118 100644 --- a/install/installer/cmd/testdata/render/kind-meta/output.golden +++ b/install/installer/cmd/testdata/render/kind-meta/output.golden @@ -913,6 +913,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -2763,7 +2767,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -4384,7 +4388,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -6399,7 +6403,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 0d9d1c68723466a253996f0a96bf0682ce57b89ebcb4ae1e4a643dd788ef1cab + gitpod.io/checksum_config: 4ef10d5efbf848826fd32a67c8ae990b45cdbab8abf649101c143908aceee122 creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/kind-workspace/output.golden b/install/installer/cmd/testdata/render/kind-workspace/output.golden index 3f29185b6adacd..8854f19f9eb2fd 100644 --- a/install/installer/cmd/testdata/render/kind-workspace/output.golden +++ b/install/installer/cmd/testdata/render/kind-workspace/output.golden @@ -2132,7 +2132,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -3908,7 +3910,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: f88055f3ce58c8eca0064866deeb5131473b6ba2980dce8bf4f9989e997f1ff5 + gitpod.io/checksum_config: 714740065966c437ee71b41e8953aa6bb77ac83596e2326b267a2b954f60242d creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/minimal/output.golden b/install/installer/cmd/testdata/render/minimal/output.golden index a6a73b4e017c92..25135dcd97e73e 100644 --- a/install/installer/cmd/testdata/render/minimal/output.golden +++ b/install/installer/cmd/testdata/render/minimal/output.golden @@ -1366,6 +1366,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -3792,7 +3796,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5508,7 +5512,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -6082,7 +6086,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -9083,7 +9089,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 0d9d1c68723466a253996f0a96bf0682ce57b89ebcb4ae1e4a643dd788ef1cab + gitpod.io/checksum_config: 4ef10d5efbf848826fd32a67c8ae990b45cdbab8abf649101c143908aceee122 creationTimestamp: null labels: app: gitpod @@ -11308,7 +11314,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: f88055f3ce58c8eca0064866deeb5131473b6ba2980dce8bf4f9989e997f1ff5 + gitpod.io/checksum_config: 714740065966c437ee71b41e8953aa6bb77ac83596e2326b267a2b954f60242d creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/shortname/output.golden b/install/installer/cmd/testdata/render/shortname/output.golden index f4e7284c32e929..a76717b7718e5c 100644 --- a/install/installer/cmd/testdata/render/shortname/output.golden +++ b/install/installer/cmd/testdata/render/shortname/output.golden @@ -1366,6 +1366,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -3792,7 +3796,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5508,7 +5512,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -6082,7 +6086,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -9083,7 +9089,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 0d9d1c68723466a253996f0a96bf0682ce57b89ebcb4ae1e4a643dd788ef1cab + gitpod.io/checksum_config: 4ef10d5efbf848826fd32a67c8ae990b45cdbab8abf649101c143908aceee122 creationTimestamp: null labels: app: gitpod @@ -11308,7 +11314,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 64e51be9e5f633cf09cee6bd25b3c5d0687f9fca3e17e2daf8eeb99e21e0faa7 + gitpod.io/checksum_config: 1d3e519239eb23a1e2f00e30f73abf47a1a18a74052043e6dc8d1196eed4fd1f creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/statefulset-customization/output.golden b/install/installer/cmd/testdata/render/statefulset-customization/output.golden index d1b486b5a896a1..e6586ce87ca693 100644 --- a/install/installer/cmd/testdata/render/statefulset-customization/output.golden +++ b/install/installer/cmd/testdata/render/statefulset-customization/output.golden @@ -1366,6 +1366,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -3804,7 +3808,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5520,7 +5524,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -6094,7 +6098,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -9095,7 +9101,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 0d9d1c68723466a253996f0a96bf0682ce57b89ebcb4ae1e4a643dd788ef1cab + gitpod.io/checksum_config: 4ef10d5efbf848826fd32a67c8ae990b45cdbab8abf649101c143908aceee122 creationTimestamp: null labels: app: gitpod @@ -11320,7 +11326,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: f88055f3ce58c8eca0064866deeb5131473b6ba2980dce8bf4f9989e997f1ff5 + gitpod.io/checksum_config: 714740065966c437ee71b41e8953aa6bb77ac83596e2326b267a2b954f60242d creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/use-pod-security-policies/output.golden b/install/installer/cmd/testdata/render/use-pod-security-policies/output.golden index a8428c492315d5..f1599832cce7a7 100644 --- a/install/installer/cmd/testdata/render/use-pod-security-policies/output.golden +++ b/install/installer/cmd/testdata/render/use-pod-security-policies/output.golden @@ -1588,6 +1588,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -4125,7 +4129,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5841,7 +5845,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -6415,7 +6419,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -9527,7 +9533,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 0d9d1c68723466a253996f0a96bf0682ce57b89ebcb4ae1e4a643dd788ef1cab + gitpod.io/checksum_config: 4ef10d5efbf848826fd32a67c8ae990b45cdbab8abf649101c143908aceee122 creationTimestamp: null labels: app: gitpod @@ -11752,7 +11758,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: f88055f3ce58c8eca0064866deeb5131473b6ba2980dce8bf4f9989e997f1ff5 + gitpod.io/checksum_config: 714740065966c437ee71b41e8953aa6bb77ac83596e2326b267a2b954f60242d creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/vsxproxy-pvc/output.golden b/install/installer/cmd/testdata/render/vsxproxy-pvc/output.golden index 613e8e0fc8e743..a9f619f8d81506 100644 --- a/install/installer/cmd/testdata/render/vsxproxy-pvc/output.golden +++ b/install/installer/cmd/testdata/render/vsxproxy-pvc/output.golden @@ -1366,6 +1366,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -3794,7 +3798,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5510,7 +5514,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -6084,7 +6088,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -9073,7 +9079,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 0d9d1c68723466a253996f0a96bf0682ce57b89ebcb4ae1e4a643dd788ef1cab + gitpod.io/checksum_config: 4ef10d5efbf848826fd32a67c8ae990b45cdbab8abf649101c143908aceee122 creationTimestamp: null labels: app: gitpod @@ -11298,7 +11304,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: f88055f3ce58c8eca0064866deeb5131473b6ba2980dce8bf4f9989e997f1ff5 + gitpod.io/checksum_config: 714740065966c437ee71b41e8953aa6bb77ac83596e2326b267a2b954f60242d creationTimestamp: null labels: app: gitpod diff --git a/install/installer/cmd/testdata/render/workspace-requests-limits/output.golden b/install/installer/cmd/testdata/render/workspace-requests-limits/output.golden index 52fb1d628d6254..cd697607b39df5 100644 --- a/install/installer/cmd/testdata/render/workspace-requests-limits/output.golden +++ b/install/installer/cmd/testdata/render/workspace-requests-limits/output.golden @@ -1366,6 +1366,10 @@ data: } ], "inlineStatic": [ + { + "search": "window.location.origin;", + "replacement": "'${ide}';" + }, { "search": "${window.location.origin}", "replacement": "." @@ -3795,7 +3799,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -5511,7 +5515,7 @@ apiVersion: v1 data: config.json: |- { - "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:latest", + "gpRunImage": "registry.hub.docker.com/gitpod/gp-run:ak-test", "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test", "ideOptions": { "options": { @@ -6085,7 +6089,9 @@ data: }, "workspacePodConfig": { "theiaPort": 23000, + "ideDebugPort": 25000, "supervisorPort": 22999, + "supervisorDebugPort": 24999, "supervisorImage": "eu.gcr.io/gitpod-core-dev/build/supervisor:test" }, "builtinPages": { @@ -9086,7 +9092,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: 0d9d1c68723466a253996f0a96bf0682ce57b89ebcb4ae1e4a643dd788ef1cab + gitpod.io/checksum_config: 4ef10d5efbf848826fd32a67c8ae990b45cdbab8abf649101c143908aceee122 creationTimestamp: null labels: app: gitpod @@ -11311,7 +11317,7 @@ spec: template: metadata: annotations: - gitpod.io/checksum_config: f88055f3ce58c8eca0064866deeb5131473b6ba2980dce8bf4f9989e997f1ff5 + gitpod.io/checksum_config: 714740065966c437ee71b41e8953aa6bb77ac83596e2326b267a2b954f60242d creationTimestamp: null labels: app: gitpod diff --git a/install/installer/pkg/components/blobserve/configmap.go b/install/installer/pkg/components/blobserve/configmap.go index 7d3db32ce6372a..29b0e33d239ec8 100644 --- a/install/installer/pkg/components/blobserve/configmap.go +++ b/install/installer/pkg/components/blobserve/configmap.go @@ -74,7 +74,12 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { Replacement: fmt.Sprintf("ide.%s/code/marketplace.json", ctx.Config.Domain), Path: "/ide/out/vs/workbench/workbench.web.main.js", }}, + // TODO consider to provide it as a part of image label or rather inline ${ide} and ${supervisor} in index.html + // to decouple blobserve and code image InlineStatic: []blobserve.InlineReplacement{{ + Search: "window.location.origin;", + Replacement: "'${ide}';", + }, { Search: "${window.location.origin}", Replacement: ".", }, { diff --git a/install/installer/pkg/components/ide-service/ide_config_configmap.go b/install/installer/pkg/components/ide-service/ide_config_configmap.go index 9e1eef4f3bec90..5dcddd3949921c 100644 --- a/install/installer/pkg/components/ide-service/ide_config_configmap.go +++ b/install/installer/pkg/components/ide-service/ide_config_configmap.go @@ -54,7 +54,7 @@ func ideConfigConfigmap(ctx *common.RenderContext) ([]runtime.Object, error) { jbPluginLatestImage := resolveLatestImage(ide.JetBrainsBackendPluginImage, "latest", ctx.VersionManifest.Components.Workspace.DesktopIdeImages.JetBrainsBackendPluginLatestImage) jbLauncherImage := ctx.ImageName(ctx.Config.Repository, ide.JetBrainsLauncherImage, ctx.VersionManifest.Components.Workspace.DesktopIdeImages.JetBrainsLauncherImage.Version) idecfg := ide_config.IDEConfig{ - GpRunImage: ctx.ImageName("registry.hub.docker.com", "gitpod/gp-run", "latest"), + GpRunImage: ctx.ImageName("registry.hub.docker.com", "gitpod/gp-run", "ak-test"), SupervisorImage: ctx.ImageName(ctx.Config.Repository, workspace.SupervisorImage, ctx.VersionManifest.Components.Workspace.Supervisor.Version), IdeOptions: ide_config.IDEOptions{ Clients: map[string]ide_config.IDEClient{ diff --git a/install/installer/pkg/components/workspace/constants.go b/install/installer/pkg/components/workspace/constants.go index dfd357ac6b790a..339710b6adc016 100644 --- a/install/installer/pkg/components/workspace/constants.go +++ b/install/installer/pkg/components/workspace/constants.go @@ -13,4 +13,6 @@ const ( SupervisorImage = "supervisor" WorkspacekitImage = "workspacekit" SupervisorPort = 22999 + SupervisorDebugPort = 24999 + IDEDebugPort = 25000 ) diff --git a/install/installer/pkg/components/ws-proxy/configmap.go b/install/installer/pkg/components/ws-proxy/configmap.go index 94b120603a20f6..f5e8f950b90e5d 100644 --- a/install/installer/pkg/components/ws-proxy/configmap.go +++ b/install/installer/pkg/components/ws-proxy/configmap.go @@ -108,9 +108,11 @@ func configmap(ctx *common.RenderContext) ([]runtime.Object, error) { WorkspaceHostSuffixRegex: gitpodInstallationWorkspaceHostSuffixRegex, }, WorkspacePodConfig: &proxy.WorkspacePodConfig{ - TheiaPort: workspace.ContainerPort, - SupervisorPort: workspace.SupervisorPort, - SupervisorImage: ctx.ImageName(ctx.Config.Repository, workspace.SupervisorImage, ctx.VersionManifest.Components.Workspace.Supervisor.Version), + TheiaPort: workspace.ContainerPort, + IDEDebugPort: workspace.IDEDebugPort, + SupervisorPort: workspace.SupervisorPort, + SupervisorDebugPort: workspace.SupervisorDebugPort, + SupervisorImage: ctx.ImageName(ctx.Config.Repository, workspace.SupervisorImage, ctx.VersionManifest.Components.Workspace.Supervisor.Version), }, BuiltinPages: proxy.BuiltinPagesConfig{ Location: "/app/public",