From c0f69ae8b45aecd4a62ba9c44e2c2c5dc41b48a1 Mon Sep 17 00:00:00 2001 From: waTeim Date: Tue, 10 Oct 2023 14:17:31 -0400 Subject: [PATCH 1/3] Alter readiness check to use the is_ready API endpoint instead of blindly trying the application frontend. --- src/contexts/workspaces-context/api.ts | 8 +++++++- src/contexts/workspaces-context/api.types.ts | 7 ++++++- src/views/splash-screen.js | 15 +++++++++------ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/contexts/workspaces-context/api.ts b/src/contexts/workspaces-context/api.ts index 5576cdb6..5fcb9ce2 100644 --- a/src/contexts/workspaces-context/api.ts +++ b/src/contexts/workspaces-context/api.ts @@ -316,6 +316,12 @@ export class WorkspacesAPI implements IWorkspacesAPI { await this.axios.delete(`/instances/${ sid }`) } + @APIRequest() + async getAppInstanceIsReady(sid: string, fetchOptions: AxiosRequestConfig={}): Promise { + const res = await this.axios.get(`/instances/${ sid }/is_ready/`); + return res.data; + } + @APIRequest() async updateAppInstance( sid: string, @@ -416,4 +422,4 @@ export class WorkspacesAPI implements IWorkspacesAPI { }; return descriptor; }; -} \ No newline at end of file +} diff --git a/src/contexts/workspaces-context/api.types.ts b/src/contexts/workspaces-context/api.types.ts index a94013d8..6c01c8b3 100644 --- a/src/contexts/workspaces-context/api.types.ts +++ b/src/contexts/workspaces-context/api.types.ts @@ -139,6 +139,7 @@ export interface AppInstance { cpus: number gpus: number memory: string + is_ready: boolean url: string status: string } @@ -146,6 +147,10 @@ export interface AvailableAppsResponse { [appName: string]: AvailableApp } export type AppInstancesResponse = AppInstance[] +export interface AppInstanceIsReadyResponse { + is_ready: boolean +} + export interface UpdateAppInstanceResponse { status: "success" | "error" message: string @@ -227,4 +232,4 @@ export interface IWorkspacesAPI { updateAppInstance(sid: string, workspace: string, cpu: string, gpu: string, memory: string, fetchOptions?: AxiosRequestConfig): Promise launchApp(appId: string, cpus: number, gpus: number, memory: string, fetchOptions?: AxiosRequestConfig): Promise getAppReady(appUrl: string, fetchOptions?: AxiosRequestConfig): Promise -} \ No newline at end of file +} diff --git a/src/views/splash-screen.js b/src/views/splash-screen.js index 7bd5504a..89c32eb5 100644 --- a/src/views/splash-screen.js +++ b/src/views/splash-screen.js @@ -15,7 +15,7 @@ export const SplashScreenView = withWorkspaceAuthentication((props) => { if(header !== null) header.style.visibility = "hidden"; if(sidePanel !== null) sidePanel.style.visibility = "hidden"; const { context } = useEnvironment(); - const { appstoreContext } = useWorkspacesAPI() + const { api, appstoreContext } = useWorkspacesAPI() const [loading, setLoading] = useState(true); const [errorPresent, setErrorPresent] = useState(false); @@ -32,15 +32,18 @@ export const SplashScreenView = withWorkspaceAuthentication((props) => { let shouldCancel = false; (async () => { try { + let parts = decoded_url.split('/'); + let sid = (parts.length >= 2)?parts[parts.length - 2]:""; + await callWithRetry(async () => { - const res = await axios.get(decoded_url) - if (res.status === 200 && shouldCancel === false) { + const res = await api.getAppInstanceIsReady(sid); + if (res.is_ready == true && shouldCancel === false) { setLoading(false) } else { - throw new Error("Could not ensure readiness of app URL") + throw new Error("app not ready") } }, { - failedCallback: () => { + failedCallback: (e) => { return shouldCancel }, timeout: 240000, @@ -88,4 +91,4 @@ export const SplashScreenView = withWorkspaceAuthentication((props) => { window.location = decoded_url; } -}) \ No newline at end of file +}) From 746b238abbb506c56252ad040ca44c0dd544829f Mon Sep 17 00:00:00 2001 From: waTeim Date: Wed, 11 Oct 2023 22:10:37 -0400 Subject: [PATCH 2/3] feature: refactor to use getAppReady and re-implement that function to use new endpoint --- src/contexts/instance-context.js | 2 +- src/contexts/workspaces-context/api.ts | 25 +++++++++++-------------- src/views/splash-screen.js | 7 ++----- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/contexts/instance-context.js b/src/contexts/instance-context.js index f647fba4..8d846edd 100644 --- a/src/contexts/instance-context.js +++ b/src/contexts/instance-context.js @@ -30,7 +30,7 @@ export const InstanceProvider = ({ children }) => { // let ready = false; const decoded_url = decodeURIComponent(app_url); const executePoll = async () => { - const isAppReady = await api.getAppReady(app_url); + const isAppReady = await api.getAppReady(decoded_url); if (isAppReady) { let newActivity = { 'sid': sid, diff --git a/src/contexts/workspaces-context/api.ts b/src/contexts/workspaces-context/api.ts index 5fcb9ce2..53046f3e 100644 --- a/src/contexts/workspaces-context/api.ts +++ b/src/contexts/workspaces-context/api.ts @@ -316,12 +316,6 @@ export class WorkspacesAPI implements IWorkspacesAPI { await this.axios.delete(`/instances/${ sid }`) } - @APIRequest() - async getAppInstanceIsReady(sid: string, fetchOptions: AxiosRequestConfig={}): Promise { - const res = await this.axios.get(`/instances/${ sid }/is_ready/`); - return res.data; - } - @APIRequest() async updateAppInstance( sid: string, @@ -362,19 +356,22 @@ export class WorkspacesAPI implements IWorkspacesAPI { } @APIRequest() - async getAppReady(appUrl: string, fetchOptions: AxiosRequestConfig={}): Promise { + async getAppReady(decodedURL: string, fetchOptions: AxiosRequestConfig={}): Promise { + let parts = decodedURL.split('/'); + let sid = (parts.length >= 2)?parts[parts.length - 2]:""; + + if(sid.length == 0) return false; try { - // appUrl is a full URL, not a relative path. - const res = await this.axios.get(appUrl, { - ...fetchOptions, - baseURL: undefined - }) - return res.status === 200 + const res = await this.axios.get(`/instances/${ sid }/is_ready/`, { + ...fetchOptions + }); + if(res.data) return res.data.is_ready; + return false; } catch (e: any) { + console.log("error getting ready status",e) return false } } - } /** diff --git a/src/views/splash-screen.js b/src/views/splash-screen.js index 89c32eb5..c8981474 100644 --- a/src/views/splash-screen.js +++ b/src/views/splash-screen.js @@ -32,12 +32,9 @@ export const SplashScreenView = withWorkspaceAuthentication((props) => { let shouldCancel = false; (async () => { try { - let parts = decoded_url.split('/'); - let sid = (parts.length >= 2)?parts[parts.length - 2]:""; - await callWithRetry(async () => { - const res = await api.getAppInstanceIsReady(sid); - if (res.is_ready == true && shouldCancel === false) { + const is_ready = await api.getAppReady(decoded_url); + if (is_ready && shouldCancel === false) { setLoading(false) } else { throw new Error("app not ready") From 2c956adc937d59e8f87bb73e54d91fa48b823d87 Mon Sep 17 00:00:00 2001 From: waTeim Date: Thu, 12 Oct 2023 15:47:47 -0400 Subject: [PATCH 3/3] remove try-catch from getAppReady; catch exceptions elsewhere --- src/contexts/instance-context.js | 7 +++++-- src/contexts/workspaces-context/api.ts | 15 +++++---------- src/views/splash-screen.js | 4 ++-- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/contexts/instance-context.js b/src/contexts/instance-context.js index 8d846edd..df501942 100644 --- a/src/contexts/instance-context.js +++ b/src/contexts/instance-context.js @@ -27,10 +27,13 @@ export const InstanceProvider = ({ children }) => { // Clear its timeout when the instance is deleted by user. const pollingInstance = (app_id, sid, app_url, app_name) => { - // let ready = false; const decoded_url = decodeURIComponent(app_url); const executePoll = async () => { - const isAppReady = await api.getAppReady(decoded_url); + let isAppReady = false; + + try { + isAppReady = await api.getAppReady(decoded_url); + } catch(e) {} // just absorb the exception, the default false is correct here if (isAppReady) { let newActivity = { 'sid': sid, diff --git a/src/contexts/workspaces-context/api.ts b/src/contexts/workspaces-context/api.ts index 53046f3e..f8f4d38b 100644 --- a/src/contexts/workspaces-context/api.ts +++ b/src/contexts/workspaces-context/api.ts @@ -361,16 +361,11 @@ export class WorkspacesAPI implements IWorkspacesAPI { let sid = (parts.length >= 2)?parts[parts.length - 2]:""; if(sid.length == 0) return false; - try { - const res = await this.axios.get(`/instances/${ sid }/is_ready/`, { - ...fetchOptions - }); - if(res.data) return res.data.is_ready; - return false; - } catch (e: any) { - console.log("error getting ready status",e) - return false - } + const res = await this.axios.get(`/instances/${ sid }/is_ready/`, { + ...fetchOptions + }); + if(res.data) return res.data.is_ready; + return false; } } diff --git a/src/views/splash-screen.js b/src/views/splash-screen.js index c8981474..b5352b51 100644 --- a/src/views/splash-screen.js +++ b/src/views/splash-screen.js @@ -33,8 +33,8 @@ export const SplashScreenView = withWorkspaceAuthentication((props) => { (async () => { try { await callWithRetry(async () => { - const is_ready = await api.getAppReady(decoded_url); - if (is_ready && shouldCancel === false) { + const isReady = await api.getAppReady(decoded_url); + if (isReady && shouldCancel === false) { setLoading(false) } else { throw new Error("app not ready")