mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-03-13 13:25:31 +00:00
Phoenix Reconnect Integration (#1123)
* Reconnect integration * git connection issue * format issue * Typo issue * added constants * Removed math.round for remainingTime * code refctor for container status check * disconnect text change
This commit is contained in:
parent
361ac45e52
commit
22da3b90ef
@ -347,6 +347,11 @@ export enum ConnectionStatusType {
|
|||||||
ReConnect = "Reconnect",
|
ReConnect = "Reconnect",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ContainerStatusType {
|
||||||
|
Active = "Active",
|
||||||
|
InActive = "InActive",
|
||||||
|
}
|
||||||
|
|
||||||
export const EmulatorMasterKey =
|
export const EmulatorMasterKey =
|
||||||
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
//[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="Well known public masterKey for emulator")]
|
||||||
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==";
|
||||||
@ -357,20 +362,23 @@ export const StyleConstants = require("less-vars-loader!../../less/Common/Consta
|
|||||||
export class Notebook {
|
export class Notebook {
|
||||||
public static readonly defaultBasePath = "./notebooks";
|
public static readonly defaultBasePath = "./notebooks";
|
||||||
public static readonly heartbeatDelayMs = 60000;
|
public static readonly heartbeatDelayMs = 60000;
|
||||||
|
public static readonly containerStatusHeartbeatDelayMs = 30000;
|
||||||
public static readonly kernelRestartInitialDelayMs = 1000;
|
public static readonly kernelRestartInitialDelayMs = 1000;
|
||||||
public static readonly kernelRestartMaxDelayMs = 20000;
|
public static readonly kernelRestartMaxDelayMs = 20000;
|
||||||
public static readonly autoSaveIntervalMs = 120000;
|
public static readonly autoSaveIntervalMs = 120000;
|
||||||
public static readonly memoryGuageToGB = 1048576;
|
public static readonly memoryGuageToGB = 1048576;
|
||||||
|
public static readonly lowMemoryBar = 0.8;
|
||||||
|
public static readonly reminingTimeMin = 10;
|
||||||
public static readonly temporarilyDownMsg = "Notebooks is currently not available. We are working on it.";
|
public static readonly temporarilyDownMsg = "Notebooks is currently not available. We are working on it.";
|
||||||
public static readonly mongoShellTemporarilyDownMsg =
|
public static readonly mongoShellTemporarilyDownMsg =
|
||||||
"We have identified an issue with the Mongo Shell and it is unavailable right now. We are actively working on the mitigation.";
|
"We have identified an issue with the Mongo Shell and it is unavailable right now. We are actively working on the mitigation.";
|
||||||
public static readonly cassandraShellTemporarilyDownMsg =
|
public static readonly cassandraShellTemporarilyDownMsg =
|
||||||
"We have identified an issue with the Cassandra Shell and it is unavailable right now. We are actively working on the mitigation.";
|
"We have identified an issue with the Cassandra Shell and it is unavailable right now. We are actively working on the mitigation.";
|
||||||
public static saveNotebookModalTitle = "Save Notebook in temporary workspace";
|
public static saveNotebookModalTitle = "Save notebook in temporary workspace";
|
||||||
public static saveNotebookModalContent =
|
public static saveNotebookModalContent =
|
||||||
"This notebook will be saved in the temporary workspace and will be removed when the session expires. To save your work permanently, save your notebooks to a GitHub repository or download the notebooks to your local machine before the session ends.";
|
"This notebook will be saved in the temporary workspace and will be removed when the session expires. To save your work permanently, save your notebooks to a GitHub repository or download the notebooks to your local machine before the session ends.";
|
||||||
public static newNotebookModalTitle = "Create Notebook in temporary workspace";
|
public static newNotebookModalTitle = "Create notebook in temporary workspace";
|
||||||
public static newNotebookUploadModalTitle = "Upload Notebook in temporary workspace";
|
public static newNotebookUploadModalTitle = "Upload notebook to temporary workspace";
|
||||||
public static newNotebookModalContent1 =
|
public static newNotebookModalContent1 =
|
||||||
"A temporary workspace will be created to enable you to work with notebooks. When the session expires, any notebooks in the workspace will be removed.";
|
"A temporary workspace will be created to enable you to work with notebooks. When the session expires, any notebooks in the workspace will be removed.";
|
||||||
public static newNotebookModalContent2 =
|
public static newNotebookModalContent2 =
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { ConnectionStatusType } from "../Common/Constants";
|
import { ConnectionStatusType, ContainerStatusType } from "../Common/Constants";
|
||||||
|
|
||||||
export interface DatabaseAccount {
|
export interface DatabaseAccount {
|
||||||
id: string;
|
id: string;
|
||||||
@ -426,6 +426,13 @@ export interface OperationStatus {
|
|||||||
export interface NotebookWorkspaceConnectionInfo {
|
export interface NotebookWorkspaceConnectionInfo {
|
||||||
authToken: string;
|
authToken: string;
|
||||||
notebookServerEndpoint: string;
|
notebookServerEndpoint: string;
|
||||||
|
forwardingId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ContainerInfo {
|
||||||
|
durationLeftMin: number;
|
||||||
|
notebookServerInfo: NotebookWorkspaceConnectionInfo;
|
||||||
|
status: ContainerStatusType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotebookWorkspaceFeedResponse {
|
export interface NotebookWorkspaceFeedResponse {
|
||||||
|
@ -13,7 +13,6 @@ import {
|
|||||||
Link,
|
Link,
|
||||||
PrimaryButton,
|
PrimaryButton,
|
||||||
ProgressIndicator,
|
ProgressIndicator,
|
||||||
Text,
|
|
||||||
TextField,
|
TextField,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
@ -197,7 +196,7 @@ export const Dialog: FC = () => {
|
|||||||
{linkProps.linkText} <FontIcon iconName="NavigateExternalInline" />
|
{linkProps.linkText} <FontIcon iconName="NavigateExternalInline" />
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
{contentHtml && <Text>{contentHtml}</Text>}
|
{contentHtml}
|
||||||
{progressIndicatorProps && <ProgressIndicator {...progressIndicatorProps} />}
|
{progressIndicatorProps && <ProgressIndicator {...progressIndicatorProps} />}
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<PrimaryButton {...primaryButtonProps} />
|
<PrimaryButton {...primaryButtonProps} />
|
||||||
|
@ -35,16 +35,19 @@ const testCassandraAccount: DataModels.DatabaseAccount = {
|
|||||||
const testNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = {
|
const testNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = {
|
||||||
authToken: "authToken",
|
authToken: "authToken",
|
||||||
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com",
|
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com",
|
||||||
|
forwardingId: "Id",
|
||||||
};
|
};
|
||||||
|
|
||||||
const testMongoNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = {
|
const testMongoNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = {
|
||||||
authToken: "authToken",
|
authToken: "authToken",
|
||||||
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/mongo",
|
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/mongo",
|
||||||
|
forwardingId: "Id",
|
||||||
};
|
};
|
||||||
|
|
||||||
const testCassandraNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = {
|
const testCassandraNotebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo = {
|
||||||
authToken: "authToken",
|
authToken: "authToken",
|
||||||
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/cassandra",
|
notebookServerEndpoint: "https://testNotebookServerEndpoint.azure.com/cassandra",
|
||||||
|
forwardingId: "Id",
|
||||||
};
|
};
|
||||||
|
|
||||||
describe("NotebookTerminalComponent", () => {
|
describe("NotebookTerminalComponent", () => {
|
||||||
|
@ -53,7 +53,7 @@ export class NotebookViewerComponent
|
|||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.clientManager = new NotebookClientV2({
|
this.clientManager = new NotebookClientV2({
|
||||||
connectionInfo: { authToken: undefined, notebookServerEndpoint: undefined },
|
connectionInfo: { authToken: undefined, notebookServerEndpoint: undefined, forwardingId: undefined },
|
||||||
databaseAccountName: undefined,
|
databaseAccountName: undefined,
|
||||||
defaultExperience: "NotebookViewer",
|
defaultExperience: "NotebookViewer",
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
|
@ -3,6 +3,7 @@ import { isPublicInternetAccessAllowed } from "Common/DatabaseAccountUtility";
|
|||||||
import * as ko from "knockout";
|
import * as ko from "knockout";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
|
import shallow from "zustand/shallow";
|
||||||
import { AuthType } from "../AuthType";
|
import { AuthType } from "../AuthType";
|
||||||
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
|
import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer";
|
||||||
import * as Constants from "../Common/Constants";
|
import * as Constants from "../Common/Constants";
|
||||||
@ -165,11 +166,9 @@ export default class Explorer {
|
|||||||
);
|
);
|
||||||
|
|
||||||
useNotebook.subscribe(
|
useNotebook.subscribe(
|
||||||
async () => {
|
async () => this.initiateAndRefreshNotebookList(),
|
||||||
this.initiateAndRefreshNotebookList();
|
(state) => [state.isNotebookEnabled, state.isRefreshed],
|
||||||
useNotebook.getState().setIsRefreshed(false);
|
shallow
|
||||||
},
|
|
||||||
(state) => state.isNotebookEnabled || state.isRefreshed
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.resourceTree = new ResourceTreeAdapter(this);
|
this.resourceTree = new ResourceTreeAdapter(this);
|
||||||
@ -179,6 +178,7 @@ export default class Explorer {
|
|||||||
useNotebook.getState().setNotebookServerInfo({
|
useNotebook.getState().setNotebookServerInfo({
|
||||||
notebookServerEndpoint: userContext.features.notebookServerUrl,
|
notebookServerEndpoint: userContext.features.notebookServerUrl,
|
||||||
authToken: userContext.features.notebookServerToken,
|
authToken: userContext.features.notebookServerToken,
|
||||||
|
forwardingId: undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,6 +364,7 @@ export default class Explorer {
|
|||||||
useNotebook.getState().setNotebookServerInfo({
|
useNotebook.getState().setNotebookServerInfo({
|
||||||
notebookServerEndpoint: userContext.features.notebookServerUrl || connectionInfo.notebookServerEndpoint,
|
notebookServerEndpoint: userContext.features.notebookServerUrl || connectionInfo.notebookServerEndpoint,
|
||||||
authToken: userContext.features.notebookServerToken || connectionInfo.authToken,
|
authToken: userContext.features.notebookServerToken || connectionInfo.authToken,
|
||||||
|
forwardingId: undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,11 +396,18 @@ export default class Explorer {
|
|||||||
connectionInfo.data &&
|
connectionInfo.data &&
|
||||||
connectionInfo.data.notebookServerUrl
|
connectionInfo.data.notebookServerUrl
|
||||||
) {
|
) {
|
||||||
|
const containerData = {
|
||||||
|
forwardingId: connectionInfo.data.forwardingId,
|
||||||
|
dbAccountName: userContext.databaseAccount.name,
|
||||||
|
};
|
||||||
|
await this.phoenixClient.initiateContainerHeartBeat(containerData);
|
||||||
|
|
||||||
connectionStatus.status = ConnectionStatusType.Connected;
|
connectionStatus.status = ConnectionStatusType.Connected;
|
||||||
useNotebook.getState().setConnectionInfo(connectionStatus);
|
useNotebook.getState().setConnectionInfo(connectionStatus);
|
||||||
useNotebook.getState().setNotebookServerInfo({
|
useNotebook.getState().setNotebookServerInfo({
|
||||||
notebookServerEndpoint: userContext.features.notebookServerUrl || connectionInfo.data.notebookServerUrl,
|
notebookServerEndpoint: userContext.features.notebookServerUrl || connectionInfo.data.notebookServerUrl,
|
||||||
authToken: userContext.features.notebookServerToken || connectionInfo.data.notebookAuthToken,
|
authToken: userContext.features.notebookServerToken || connectionInfo.data.notebookAuthToken,
|
||||||
|
forwardingId: connectionInfo.data.forwardingId,
|
||||||
});
|
});
|
||||||
this.notebookManager?.notebookClient
|
this.notebookManager?.notebookClient
|
||||||
.getMemoryUsage()
|
.getMemoryUsage()
|
||||||
@ -407,11 +415,11 @@ export default class Explorer {
|
|||||||
useNotebook.getState().setIsAllocating(false);
|
useNotebook.getState().setIsAllocating(false);
|
||||||
} else {
|
} else {
|
||||||
connectionStatus.status = ConnectionStatusType.Failed;
|
connectionStatus.status = ConnectionStatusType.Failed;
|
||||||
useNotebook.getState().resetConatinerConnection(connectionStatus);
|
useNotebook.getState().resetContainerConnection(connectionStatus);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
connectionStatus.status = ConnectionStatusType.Failed;
|
connectionStatus.status = ConnectionStatusType.Failed;
|
||||||
useNotebook.getState().resetConatinerConnection(connectionStatus);
|
useNotebook.getState().resetContainerConnection(connectionStatus);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
this.refreshNotebookList();
|
this.refreshNotebookList();
|
||||||
@ -692,7 +700,7 @@ export default class Explorer {
|
|||||||
throw new Error(`Invalid notebookContentItem: ${notebookContentItem}`);
|
throw new Error(`Invalid notebookContentItem: ${notebookContentItem}`);
|
||||||
}
|
}
|
||||||
if (notebookContentItem.type === NotebookContentItemType.Notebook && NotebookUtil.isPhoenixEnabled()) {
|
if (notebookContentItem.type === NotebookContentItemType.Notebook && NotebookUtil.isPhoenixEnabled()) {
|
||||||
this.allocateContainer();
|
await this.allocateContainer();
|
||||||
}
|
}
|
||||||
|
|
||||||
const notebookTabs = useTabs
|
const notebookTabs = useTabs
|
||||||
@ -1016,8 +1024,8 @@ export default class Explorer {
|
|||||||
useDialog
|
useDialog
|
||||||
.getState()
|
.getState()
|
||||||
.showOkModalDialog(
|
.showOkModalDialog(
|
||||||
"Failed to Connect",
|
"Failed to connect",
|
||||||
"Failed to connect temporary workspace, this could happen because of network issue please refresh and try again."
|
"Failed to connect to temporary workspace. This could happen because of network issues. Please refresh the page and try again."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,8 +1,20 @@
|
|||||||
import { Icon, ProgressIndicator, Stack, TooltipHost } from "@fluentui/react";
|
import {
|
||||||
import { ActionButton } from "@fluentui/react/lib/Button";
|
FocusTrapCallout,
|
||||||
|
FocusZone,
|
||||||
|
FocusZoneTabbableElements,
|
||||||
|
FontWeights,
|
||||||
|
Icon,
|
||||||
|
mergeStyleSets,
|
||||||
|
ProgressIndicator,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
TooltipHost,
|
||||||
|
} from "@fluentui/react";
|
||||||
|
import { useId } from "@fluentui/react-hooks";
|
||||||
|
import { ActionButton, DefaultButton } from "@fluentui/react/lib/Button";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import "../../../../less/hostedexplorer.less";
|
import "../../../../less/hostedexplorer.less";
|
||||||
import { ConnectionStatusType, Notebook } from "../../../Common/Constants";
|
import { ConnectionStatusType, ContainerStatusType, Notebook } from "../../../Common/Constants";
|
||||||
import Explorer from "../../Explorer";
|
import Explorer from "../../Explorer";
|
||||||
import { useNotebook } from "../../Notebook/useNotebook";
|
import { useNotebook } from "../../Notebook/useNotebook";
|
||||||
import "../CommandBar/ConnectionStatusComponent.less";
|
import "../CommandBar/ConnectionStatusComponent.less";
|
||||||
@ -16,6 +28,26 @@ export const ConnectionStatus: React.FC<Props> = ({ container }: Props): JSX.Ele
|
|||||||
const [counter, setCounter] = React.useState(0);
|
const [counter, setCounter] = React.useState(0);
|
||||||
const [statusColor, setStatusColor] = React.useState("");
|
const [statusColor, setStatusColor] = React.useState("");
|
||||||
const [toolTipContent, setToolTipContent] = React.useState("Connect to temporary workspace.");
|
const [toolTipContent, setToolTipContent] = React.useState("Connect to temporary workspace.");
|
||||||
|
const [isBarDismissed, setIsBarDismissed] = React.useState<boolean>(false);
|
||||||
|
const buttonId = useId("callout-button");
|
||||||
|
const containerInfo = useNotebook((state) => state.containerStatus);
|
||||||
|
|
||||||
|
const styles = mergeStyleSets({
|
||||||
|
callout: {
|
||||||
|
width: 320,
|
||||||
|
padding: "20px 24px",
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
marginBottom: 12,
|
||||||
|
fontWeight: FontWeights.semilight,
|
||||||
|
},
|
||||||
|
buttons: {
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
marginTop: 20,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
let intervalId: NodeJS.Timeout;
|
let intervalId: NodeJS.Timeout;
|
||||||
|
|
||||||
@ -78,13 +110,25 @@ export const ConnectionStatus: React.FC<Props> = ({ container }: Props): JSX.Ele
|
|||||||
setToolTipContent("Click here to Reconnect to temporary workspace.");
|
setToolTipContent("Click here to Reconnect to temporary workspace.");
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
<TooltipHost
|
||||||
|
content={
|
||||||
|
containerInfo.status &&
|
||||||
|
containerInfo.status === ContainerStatusType.Active &&
|
||||||
|
containerInfo.durationLeftMin <= Notebook.reminingTimeMin
|
||||||
|
? `Connected to temporary workspace. This temporary workspace will get deleted in ${Math.round(
|
||||||
|
containerInfo.durationLeftMin
|
||||||
|
)} minutes.`
|
||||||
|
: toolTipContent
|
||||||
|
}
|
||||||
|
>
|
||||||
<ActionButton
|
<ActionButton
|
||||||
|
id={buttonId}
|
||||||
className={connectionInfo.status === ConnectionStatusType.Failed ? "commandReactBtn" : "connectedReactBtn"}
|
className={connectionInfo.status === ConnectionStatusType.Failed ? "commandReactBtn" : "connectedReactBtn"}
|
||||||
onClick={(e: React.MouseEvent<HTMLSpanElement>) =>
|
onClick={(e: React.MouseEvent<HTMLSpanElement>) =>
|
||||||
connectionInfo.status === ConnectionStatusType.Failed ? container.allocateContainer() : e.preventDefault()
|
connectionInfo.status === ConnectionStatusType.Failed ? container.allocateContainer() : e.preventDefault()
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<TooltipHost content={toolTipContent}>
|
|
||||||
<Stack className="connectionStatusContainer" horizontal>
|
<Stack className="connectionStatusContainer" horizontal>
|
||||||
<i className={statusColor}></i>
|
<i className={statusColor}></i>
|
||||||
<span className={connectionInfo.status === ConnectionStatusType.Failed ? "connectionStatusFailed" : ""}>
|
<span className={connectionInfo.status === ConnectionStatusType.Failed ? "connectionStatusFailed" : ""}>
|
||||||
@ -95,13 +139,41 @@ export const ConnectionStatus: React.FC<Props> = ({ container }: Props): JSX.Ele
|
|||||||
)}
|
)}
|
||||||
{connectionInfo.status === ConnectionStatusType.Connected && !isActive && (
|
{connectionInfo.status === ConnectionStatusType.Connected && !isActive && (
|
||||||
<ProgressIndicator
|
<ProgressIndicator
|
||||||
className={usedGB / totalGB > 0.8 ? "lowMemory" : ""}
|
className={usedGB / totalGB > Notebook.lowMemoryBar ? "lowMemory" : ""}
|
||||||
description={usedGB.toFixed(1) + " of " + totalGB.toFixed(1) + " GB"}
|
description={usedGB.toFixed(1) + " of " + totalGB.toFixed(1) + " GB"}
|
||||||
percentComplete={usedGB / totalGB}
|
percentComplete={usedGB / totalGB}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</TooltipHost>
|
{!isBarDismissed &&
|
||||||
|
containerInfo.status &&
|
||||||
|
containerInfo.status === ContainerStatusType.Active &&
|
||||||
|
containerInfo.durationLeftMin <= Notebook.reminingTimeMin ? (
|
||||||
|
<FocusTrapCallout
|
||||||
|
role="alertdialog"
|
||||||
|
className={styles.callout}
|
||||||
|
gapSpace={0}
|
||||||
|
target={`#${buttonId}`}
|
||||||
|
onDismiss={() => setIsBarDismissed(true)}
|
||||||
|
setInitialFocus
|
||||||
|
>
|
||||||
|
<Text block variant="xLarge" className={styles.title}>
|
||||||
|
Remaining time
|
||||||
|
</Text>
|
||||||
|
<Text block variant="small">
|
||||||
|
This temporary workspace will get deleted in {Math.round(containerInfo.durationLeftMin)} minutes. To
|
||||||
|
save your work permanently, save your notebooks to a GitHub repository or download the notebooks to your
|
||||||
|
local machine before the session ends.
|
||||||
|
</Text>
|
||||||
|
<FocusZone handleTabKey={FocusZoneTabbableElements.all} isCircularNavigation>
|
||||||
|
<Stack className={styles.buttons} gap={8} horizontal>
|
||||||
|
<DefaultButton onClick={() => setIsBarDismissed(true)}>Dismiss</DefaultButton>
|
||||||
|
</Stack>
|
||||||
|
</FocusZone>
|
||||||
|
</FocusTrapCallout>
|
||||||
|
) : undefined}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
|
</TooltipHost>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -59,6 +59,7 @@ export class NotebookContainerClient {
|
|||||||
|
|
||||||
const { notebookServerEndpoint, authToken } = this.getNotebookServerConfig();
|
const { notebookServerEndpoint, authToken } = this.getNotebookServerConfig();
|
||||||
try {
|
try {
|
||||||
|
if (this.checkStatus()) {
|
||||||
const response = await fetch(`${notebookServerEndpoint}api/metrics/memory`, {
|
const response = await fetch(`${notebookServerEndpoint}api/metrics/memory`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
@ -78,12 +79,7 @@ export class NotebookContainerClient {
|
|||||||
freeKB: memoryUsageInfo.free,
|
freeKB: memoryUsageInfo.free,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (NotebookUtil.isPhoenixEnabled()) {
|
}
|
||||||
const connectionStatus: ContainerConnectionInfo = {
|
|
||||||
status: ConnectionStatusType.ReConnect,
|
|
||||||
};
|
|
||||||
useNotebook.getState().resetConatinerConnection(connectionStatus);
|
|
||||||
useNotebook.getState().setIsRefreshed(true);
|
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -97,14 +93,30 @@ export class NotebookContainerClient {
|
|||||||
const connectionStatus: ContainerConnectionInfo = {
|
const connectionStatus: ContainerConnectionInfo = {
|
||||||
status: ConnectionStatusType.Failed,
|
status: ConnectionStatusType.Failed,
|
||||||
};
|
};
|
||||||
useNotebook.getState().resetConatinerConnection(connectionStatus);
|
useNotebook.getState().resetContainerConnection(connectionStatus);
|
||||||
useNotebook.getState().setIsRefreshed(true);
|
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
|
||||||
}
|
}
|
||||||
this.onConnectionLost();
|
this.onConnectionLost();
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private checkStatus(): boolean {
|
||||||
|
if (NotebookUtil.isPhoenixEnabled()) {
|
||||||
|
if (
|
||||||
|
useNotebook.getState().containerStatus?.status &&
|
||||||
|
useNotebook.getState().containerStatus?.status === Constants.ContainerStatusType.InActive
|
||||||
|
) {
|
||||||
|
const connectionStatus: ContainerConnectionInfo = {
|
||||||
|
status: ConnectionStatusType.ReConnect,
|
||||||
|
};
|
||||||
|
useNotebook.getState().resetContainerConnection(connectionStatus);
|
||||||
|
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
public async resetWorkspace(): Promise<void> {
|
public async resetWorkspace(): Promise<void> {
|
||||||
this.isResettingWorkspace = true;
|
this.isResettingWorkspace = true;
|
||||||
try {
|
try {
|
||||||
|
@ -35,6 +35,7 @@ describe("auto start kernel", () => {
|
|||||||
connectionInfo: {
|
connectionInfo: {
|
||||||
authToken: "autToken",
|
authToken: "autToken",
|
||||||
notebookServerEndpoint: "notebookServerEndpoint",
|
notebookServerEndpoint: "notebookServerEndpoint",
|
||||||
|
forwardingId: "Id",
|
||||||
},
|
},
|
||||||
databaseAccountName: undefined,
|
databaseAccountName: undefined,
|
||||||
defaultExperience: undefined,
|
defaultExperience: undefined,
|
||||||
|
@ -7,7 +7,7 @@ import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
|
|||||||
import * as Logger from "../../Common/Logger";
|
import * as Logger from "../../Common/Logger";
|
||||||
import { configContext } from "../../ConfigContext";
|
import { configContext } from "../../ConfigContext";
|
||||||
import * as DataModels from "../../Contracts/DataModels";
|
import * as DataModels from "../../Contracts/DataModels";
|
||||||
import { ContainerConnectionInfo } from "../../Contracts/DataModels";
|
import { ContainerConnectionInfo, ContainerInfo } from "../../Contracts/DataModels";
|
||||||
import { IPinnedRepo } from "../../Juno/JunoClient";
|
import { IPinnedRepo } from "../../Juno/JunoClient";
|
||||||
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
|
||||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||||
@ -35,6 +35,7 @@ interface NotebookState {
|
|||||||
notebookFolderName: string;
|
notebookFolderName: string;
|
||||||
isAllocating: boolean;
|
isAllocating: boolean;
|
||||||
isRefreshed: boolean;
|
isRefreshed: boolean;
|
||||||
|
containerStatus: ContainerInfo;
|
||||||
setIsNotebookEnabled: (isNotebookEnabled: boolean) => void;
|
setIsNotebookEnabled: (isNotebookEnabled: boolean) => void;
|
||||||
setIsNotebooksEnabledForAccount: (isNotebooksEnabledForAccount: boolean) => void;
|
setIsNotebooksEnabledForAccount: (isNotebooksEnabledForAccount: boolean) => void;
|
||||||
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) => void;
|
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) => void;
|
||||||
@ -53,8 +54,9 @@ interface NotebookState {
|
|||||||
initializeGitHubRepos: (pinnedRepos: IPinnedRepo[]) => void;
|
initializeGitHubRepos: (pinnedRepos: IPinnedRepo[]) => void;
|
||||||
setConnectionInfo: (connectionInfo: ContainerConnectionInfo) => void;
|
setConnectionInfo: (connectionInfo: ContainerConnectionInfo) => void;
|
||||||
setIsAllocating: (isAllocating: boolean) => void;
|
setIsAllocating: (isAllocating: boolean) => void;
|
||||||
resetConatinerConnection: (connectionStatus: ContainerConnectionInfo) => void;
|
resetContainerConnection: (connectionStatus: ContainerConnectionInfo) => void;
|
||||||
setIsRefreshed: (isAllocating: boolean) => void;
|
setIsRefreshed: (isAllocating: boolean) => void;
|
||||||
|
setContainerStatus: (containerStatus: ContainerInfo) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
||||||
@ -63,6 +65,7 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
|||||||
notebookServerInfo: {
|
notebookServerInfo: {
|
||||||
notebookServerEndpoint: undefined,
|
notebookServerEndpoint: undefined,
|
||||||
authToken: undefined,
|
authToken: undefined,
|
||||||
|
forwardingId: undefined,
|
||||||
},
|
},
|
||||||
sparkClusterConnectionInfo: {
|
sparkClusterConnectionInfo: {
|
||||||
userName: undefined,
|
userName: undefined,
|
||||||
@ -83,6 +86,11 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
|||||||
notebookFolderName: undefined,
|
notebookFolderName: undefined,
|
||||||
isAllocating: false,
|
isAllocating: false,
|
||||||
isRefreshed: false,
|
isRefreshed: false,
|
||||||
|
containerStatus: {
|
||||||
|
status: undefined,
|
||||||
|
durationLeftMin: undefined,
|
||||||
|
notebookServerInfo: undefined,
|
||||||
|
},
|
||||||
setIsNotebookEnabled: (isNotebookEnabled: boolean) => set({ isNotebookEnabled }),
|
setIsNotebookEnabled: (isNotebookEnabled: boolean) => set({ isNotebookEnabled }),
|
||||||
setIsNotebooksEnabledForAccount: (isNotebooksEnabledForAccount: boolean) => set({ isNotebooksEnabledForAccount }),
|
setIsNotebooksEnabledForAccount: (isNotebooksEnabledForAccount: boolean) => set({ isNotebooksEnabledForAccount }),
|
||||||
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) =>
|
setNotebookServerInfo: (notebookServerInfo: DataModels.NotebookWorkspaceConnectionInfo) =>
|
||||||
@ -270,13 +278,20 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
|
|||||||
},
|
},
|
||||||
setConnectionInfo: (connectionInfo: ContainerConnectionInfo) => set({ connectionInfo }),
|
setConnectionInfo: (connectionInfo: ContainerConnectionInfo) => set({ connectionInfo }),
|
||||||
setIsAllocating: (isAllocating: boolean) => set({ isAllocating }),
|
setIsAllocating: (isAllocating: boolean) => set({ isAllocating }),
|
||||||
resetConatinerConnection: (connectionStatus: ContainerConnectionInfo): void => {
|
resetContainerConnection: (connectionStatus: ContainerConnectionInfo): void => {
|
||||||
useNotebook.getState().setConnectionInfo(connectionStatus);
|
useNotebook.getState().setConnectionInfo(connectionStatus);
|
||||||
useNotebook.getState().setNotebookServerInfo({
|
useNotebook.getState().setNotebookServerInfo({
|
||||||
notebookServerEndpoint: undefined,
|
notebookServerEndpoint: undefined,
|
||||||
authToken: undefined,
|
authToken: undefined,
|
||||||
|
forwardingId: undefined,
|
||||||
});
|
});
|
||||||
useNotebook.getState().setIsAllocating(false);
|
useNotebook.getState().setIsAllocating(false);
|
||||||
|
useNotebook.getState().setContainerStatus({
|
||||||
|
status: undefined,
|
||||||
|
durationLeftMin: undefined,
|
||||||
|
notebookServerInfo: undefined,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
setIsRefreshed: (isRefreshed: boolean) => set({ isRefreshed }),
|
setIsRefreshed: (isRefreshed: boolean) => set({ isRefreshed }),
|
||||||
|
setContainerStatus: (containerStatus: ContainerInfo) => set({ containerStatus }),
|
||||||
}));
|
}));
|
||||||
|
@ -100,6 +100,7 @@ export default class TerminalTab extends TabsBase {
|
|||||||
return {
|
return {
|
||||||
authToken: info.authToken,
|
authToken: info.authToken,
|
||||||
notebookServerEndpoint: `${info.notebookServerEndpoint.replace(/\/+$/, "")}/${endpointSuffix}`,
|
notebookServerEndpoint: `${info.notebookServerEndpoint.replace(/\/+$/, "")}/${endpointSuffix}`,
|
||||||
|
forwardingId: info.forwardingId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import { HttpHeaders, HttpStatusCodes } from "../Common/Constants";
|
import { ContainerStatusType, HttpHeaders, HttpStatusCodes, Notebook } from "../Common/Constants";
|
||||||
|
import { getErrorMessage } from "../Common/ErrorHandlingUtils";
|
||||||
|
import * as Logger from "../Common/Logger";
|
||||||
import { configContext } from "../ConfigContext";
|
import { configContext } from "../ConfigContext";
|
||||||
|
import { ContainerInfo } from "../Contracts/DataModels";
|
||||||
|
import { useNotebook } from "../Explorer/Notebook/useNotebook";
|
||||||
import { userContext } from "../UserContext";
|
import { userContext } from "../UserContext";
|
||||||
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
|
||||||
|
|
||||||
@ -10,17 +14,24 @@ export interface IPhoenixResponse<T> {
|
|||||||
export interface IPhoenixConnectionInfoResult {
|
export interface IPhoenixConnectionInfoResult {
|
||||||
readonly notebookAuthToken?: string;
|
readonly notebookAuthToken?: string;
|
||||||
readonly notebookServerUrl?: string;
|
readonly notebookServerUrl?: string;
|
||||||
|
readonly forwardingId?: string;
|
||||||
}
|
}
|
||||||
export interface IProvosionData {
|
export interface IProvisionData {
|
||||||
cosmosEndpoint: string;
|
cosmosEndpoint: string;
|
||||||
dbAccountName: string;
|
dbAccountName: string;
|
||||||
aadToken: string;
|
aadToken: string;
|
||||||
resourceGroup: string;
|
resourceGroup: string;
|
||||||
subscriptionId: string;
|
subscriptionId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IContainerData {
|
||||||
|
dbAccountName: string;
|
||||||
|
forwardingId: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class PhoenixClient {
|
export class PhoenixClient {
|
||||||
public async containerConnectionInfo(
|
public async containerConnectionInfo(
|
||||||
provisionData: IProvosionData
|
provisionData: IProvisionData
|
||||||
): Promise<IPhoenixResponse<IPhoenixConnectionInfoResult>> {
|
): Promise<IPhoenixResponse<IPhoenixConnectionInfoResult>> {
|
||||||
try {
|
try {
|
||||||
const response = await window.fetch(`${this.getPhoenixContainerPoolingEndPoint()}/allocate`, {
|
const response = await window.fetch(`${this.getPhoenixContainerPoolingEndPoint()}/allocate`, {
|
||||||
@ -41,6 +52,60 @@ export class PhoenixClient {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public async initiateContainerHeartBeat(containerData: { forwardingId: string; dbAccountName: string }) {
|
||||||
|
this.getContainerHealth(Notebook.containerStatusHeartbeatDelayMs, containerData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private scheduleContainerHeartbeat(delayMs: number, containerData: IContainerData): void {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.getContainerHealth(delayMs, containerData);
|
||||||
|
}, delayMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getContainerStatusAsync(containerData: IContainerData): Promise<ContainerInfo> {
|
||||||
|
try {
|
||||||
|
const response = await window.fetch(
|
||||||
|
`${this.getPhoenixContainerPoolingEndPoint()}/${containerData.dbAccountName}/${containerData.forwardingId}`,
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
headers: PhoenixClient.getHeaders(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (response.status === HttpStatusCodes.OK) {
|
||||||
|
const containerStatus = await response.json();
|
||||||
|
return {
|
||||||
|
durationLeftMin: containerStatus.durationLeftInMinutes,
|
||||||
|
notebookServerInfo: containerStatus.notebookServerInfo,
|
||||||
|
status: ContainerStatusType.Active,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
durationLeftMin: undefined,
|
||||||
|
notebookServerInfo: undefined,
|
||||||
|
status: ContainerStatusType.InActive,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
Logger.logError(getErrorMessage(error), "PhoenixClient/getContainerStatus");
|
||||||
|
return {
|
||||||
|
durationLeftMin: undefined,
|
||||||
|
notebookServerInfo: undefined,
|
||||||
|
status: ContainerStatusType.InActive,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getContainerHealth(delayMs: number, containerData: { forwardingId: string; dbAccountName: string }) {
|
||||||
|
this.getContainerStatusAsync(containerData)
|
||||||
|
.then((ContainerInfo) => useNotebook.getState().setContainerStatus(ContainerInfo))
|
||||||
|
.finally(() => {
|
||||||
|
if (
|
||||||
|
useNotebook.getState().containerStatus.status &&
|
||||||
|
useNotebook.getState().containerStatus.status === ContainerStatusType.Active
|
||||||
|
) {
|
||||||
|
this.scheduleContainerHeartbeat(delayMs, containerData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static getPhoenixEndpoint(): string {
|
public static getPhoenixEndpoint(): string {
|
||||||
const phoenixEndpoint =
|
const phoenixEndpoint =
|
||||||
|
@ -239,7 +239,7 @@ export function downloadItem(
|
|||||||
useDialog
|
useDialog
|
||||||
.getState()
|
.getState()
|
||||||
.showOkModalDialog(
|
.showOkModalDialog(
|
||||||
"Failed to Connect",
|
"Failed to connect",
|
||||||
"Failed to connect to temporary workspace. Please refresh the page and try again."
|
"Failed to connect to temporary workspace. Please refresh the page and try again."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user