From 314f66c0b7f1081301c3c6849aa7cc5413a19db9 Mon Sep 17 00:00:00 2001 From: Alex Tugarev Date: Mon, 30 May 2022 15:10:29 +0000 Subject: [PATCH 1/4] [dashboard] addressing linter issues --- components/dashboard/src/start/CreateWorkspace.tsx | 10 +++++----- components/dashboard/src/start/StartWorkspace.tsx | 9 +++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/components/dashboard/src/start/CreateWorkspace.tsx b/components/dashboard/src/start/CreateWorkspace.tsx index 4221d6bfa0ade2..ce7f331a6a11bc 100644 --- a/components/dashboard/src/start/CreateWorkspace.tsx +++ b/components/dashboard/src/start/CreateWorkspace.tsx @@ -461,9 +461,9 @@ interface RunningPrebuildViewProps { } function RunningPrebuildView(props: RunningPrebuildViewProps) { - const logsEmitter = new EventEmitter(); - let pollTimeout: NodeJS.Timeout | undefined; - let prebuildDoneTriggered: boolean = false; + const [logsEmitter] = useState(new EventEmitter()); + const [pollTimeout, setPollTimeout] = useState(); + const [prebuildDoneTriggered, setPrebuildDoneTriggered] = useState(false); useEffect(() => { const checkIsPrebuildDone = async (): Promise => { @@ -476,7 +476,7 @@ function RunningPrebuildView(props: RunningPrebuildViewProps) { if (done) { // note: this treats "done" as "available" which is not equivalent. // This works because the backend ignores prebuilds which are not "available", and happily starts a workspace as if there was no prebuild at all. - prebuildDoneTriggered = true; + setPrebuildDoneTriggered(true); props.onPrebuildSucceeded(); return true; } @@ -485,7 +485,7 @@ function RunningPrebuildView(props: RunningPrebuildViewProps) { const pollIsPrebuildDone = async () => { clearTimeout(pollTimeout!); await checkIsPrebuildDone(); - pollTimeout = setTimeout(pollIsPrebuildDone, 10000); + setPollTimeout(setTimeout(pollIsPrebuildDone, 10000)); }; const disposables = watchHeadlessLogs( diff --git a/components/dashboard/src/start/StartWorkspace.tsx b/components/dashboard/src/start/StartWorkspace.tsx index 0bf31ccfda946b..821982442d2204 100644 --- a/components/dashboard/src/start/StartWorkspace.tsx +++ b/components/dashboard/src/start/StartWorkspace.tsx @@ -19,7 +19,7 @@ import { IDEOptions } from "@gitpod/gitpod-protocol/lib/ide-protocol"; import { ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error"; import EventEmitter from "events"; import * as queryString from "query-string"; -import React, { Suspense, useEffect } from "react"; +import React, { Suspense, useEffect, useState } from "react"; import { v4 } from "uuid"; import Arrow from "../components/Arrow"; import ContextMenu from "../components/ContextMenu"; @@ -661,7 +661,7 @@ interface ImageBuildViewProps { } function ImageBuildView(props: ImageBuildViewProps) { - const logsEmitter = new EventEmitter(); + const [logsEmitter] = useState(new EventEmitter()); useEffect(() => { let registered = false; @@ -669,11 +669,12 @@ function ImageBuildView(props: ImageBuildViewProps) { if (registered) { return; } + registered = true; getGitpodService() .server.watchWorkspaceImageBuildLogs(props.workspaceId) - .then(() => (registered = true)) .catch((err) => { + registered = false; if (err?.code === ErrorCodes.HEADLESS_LOG_NOT_YET_AVAILABLE) { // wait, and then retry setTimeout(watchBuild, 5000); @@ -718,7 +719,7 @@ function ImageBuildView(props: ImageBuildViewProps) { } function HeadlessWorkspaceView(props: { instanceId: string }) { - const logsEmitter = new EventEmitter(); + const [logsEmitter] = useState(new EventEmitter()); useEffect(() => { const disposables = watchHeadlessLogs( From db5409fe85d1fb1b67bd41930fc588a74139d384 Mon Sep 17 00:00:00 2001 From: Alex Tugarev Date: Mon, 30 May 2022 15:30:49 +0000 Subject: [PATCH 2/4] [dashboard] align client registration --- components/dashboard/src/start/StartWorkspace.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/components/dashboard/src/start/StartWorkspace.tsx b/components/dashboard/src/start/StartWorkspace.tsx index 821982442d2204..0acd996503a099 100644 --- a/components/dashboard/src/start/StartWorkspace.tsx +++ b/components/dashboard/src/start/StartWorkspace.tsx @@ -131,7 +131,13 @@ export default class StartWorkspace extends React.Component this.fetchWorkspaceInfo(undefined), + onInstanceUpdate: (workspaceInstance: WorkspaceInstance) => + this.onInstanceUpdate(workspaceInstance), + }), + ); } catch (error) { console.error(error); this.setState({ error }); @@ -297,10 +303,6 @@ export default class StartWorkspace extends React.Component Date: Wed, 8 Jun 2022 10:14:36 +0000 Subject: [PATCH 3/4] add "failed" to PrebuiltWorkspace.isDone --- components/gitpod-protocol/src/protocol.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/gitpod-protocol/src/protocol.ts b/components/gitpod-protocol/src/protocol.ts index c443046e729dea..38717f4e1af1ec 100644 --- a/components/gitpod-protocol/src/protocol.ts +++ b/components/gitpod-protocol/src/protocol.ts @@ -754,7 +754,9 @@ export interface PrebuiltWorkspace { export namespace PrebuiltWorkspace { export function isDone(pws: PrebuiltWorkspace) { - return pws.state === "available" || pws.state === "timeout" || pws.state === "aborted"; + return ( + pws.state === "available" || pws.state === "timeout" || pws.state === "aborted" || pws.state === "failed" + ); } export function isAvailable(pws: PrebuiltWorkspace) { From 5a6250cf5f0d575c585f50b7cc7ba66d8a8184d3 Mon Sep 17 00:00:00 2001 From: Alex Tugarev Date: Wed, 8 Jun 2022 11:32:05 +0000 Subject: [PATCH 4/4] get rid of isPrebuildDone polling --- .../dashboard/src/start/CreateWorkspace.tsx | 42 +++++++------------ 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/components/dashboard/src/start/CreateWorkspace.tsx b/components/dashboard/src/start/CreateWorkspace.tsx index ce7f331a6a11bc..9f964e8d65554f 100644 --- a/components/dashboard/src/start/CreateWorkspace.tsx +++ b/components/dashboard/src/start/CreateWorkspace.tsx @@ -462,39 +462,28 @@ interface RunningPrebuildViewProps { function RunningPrebuildView(props: RunningPrebuildViewProps) { const [logsEmitter] = useState(new EventEmitter()); - const [pollTimeout, setPollTimeout] = useState(); - const [prebuildDoneTriggered, setPrebuildDoneTriggered] = useState(false); useEffect(() => { - const checkIsPrebuildDone = async (): Promise => { - if (prebuildDoneTriggered) { - console.debug("prebuild done already triggered, doing nothing"); - return true; - } - - const done = await getGitpodService().server.isPrebuildDone(props.runningPrebuild.prebuildID); - if (done) { - // note: this treats "done" as "available" which is not equivalent. - // This works because the backend ignores prebuilds which are not "available", and happily starts a workspace as if there was no prebuild at all. - setPrebuildDoneTriggered(true); - props.onPrebuildSucceeded(); - return true; - } - return false; - }; - const pollIsPrebuildDone = async () => { - clearTimeout(pollTimeout!); - await checkIsPrebuildDone(); - setPollTimeout(setTimeout(pollIsPrebuildDone, 10000)); - }; - const disposables = watchHeadlessLogs( props.runningPrebuild.instanceID, (chunk) => logsEmitter.emit("logs", chunk), - checkIsPrebuildDone, + async () => false, ); + + disposables.push( + getGitpodService().registerClient({ + onInstanceUpdate: (update) => { + if (update.workspaceId !== props.runningPrebuild.workspaceID) { + return; + } + if (update.status.phase === "stopped") { + props.onPrebuildSucceeded(); + } + }, + }), + ); + return function cleanup() { - clearTimeout(pollTimeout!); disposables.dispose(); }; }, []); @@ -507,7 +496,6 @@ function RunningPrebuildView(props: RunningPrebuildViewProps) {