Address review coments

This commit is contained in:
kcheekuri 2021-12-11 14:11:32 -05:00
parent a4be933f70
commit 004c38c6b8
10 changed files with 47 additions and 156 deletions

View File

@ -1,3 +1,4 @@
{
"JUNO_ENDPOINT": "https://tools-staging.cosmos.azure.com"
"JUNO_ENDPOINT": "https://tools-staging.cosmos.azure.com",
"IS_MPAC" : true
}

View File

@ -1,3 +1,4 @@
{
"JUNO_ENDPOINT": "https://tools.cosmos.azure.com"
"JUNO_ENDPOINT": "https://tools.cosmos.azure.com",
"IS_MPAC" : false
}

View File

@ -24,6 +24,7 @@ export interface ConfigContext {
JUNO_ENDPOINT: string;
GITHUB_CLIENT_ID: string;
GITHUB_CLIENT_SECRET?: string; // No need to inject secret for prod. Juno already knows it.
IS_MPAC: boolean;
hostedExplorerURL: string;
armAPIVersion?: string;
allowedJunoOrigins: string[];
@ -53,6 +54,7 @@ let configContext: Readonly<ConfigContext> = {
ARCADIA_ENDPOINT: "https://workspaceartifacts.projectarcadia.net",
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
IS_MPAC: false,
JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com",
allowedJunoOrigins: [

View File

@ -441,13 +441,7 @@ export interface IProvisionData {
cosmosEndpoint: string;
}
export interface IAccountData {
subscriptionId: string;
resourceGroup: string;
dbAccountName: string;
}
export interface IContainerData extends IAccountData {
export interface IContainerData {
forwardingId: string;
}

View File

@ -16,7 +16,6 @@ import { QueriesClient } from "../Common/QueriesClient";
import * as DataModels from "../Contracts/DataModels";
import {
ContainerConnectionInfo,
IAccountData,
IContainerData,
IPhoenixConnectionInfoResult,
IProvisionData,
@ -373,18 +372,13 @@ export default class Explorer {
const provisionData: IProvisionData = {
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint,
};
const accountData: IAccountData = {
subscriptionId: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
dbAccountName: userContext.databaseAccount.name,
};
const connectionStatus: ContainerConnectionInfo = {
status: ConnectionStatusType.Connecting,
};
useNotebook.getState().setConnectionInfo(connectionStatus);
try {
useNotebook.getState().setIsAllocating(true);
const connectionInfo = await this.phoenixClient.allocateContainer(provisionData, accountData);
const connectionInfo = await this.phoenixClient.allocateContainer(provisionData);
await this.setNotebookInfo(connectionInfo, connectionStatus);
} catch (error) {
connectionStatus.status = ConnectionStatusType.Failed;
@ -404,9 +398,6 @@ export default class Explorer {
) {
if (connectionInfo.status === HttpStatusCodes.OK && connectionInfo.data && connectionInfo.data.notebookServerUrl) {
const containerData: IContainerData = {
subscriptionId: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
dbAccountName: userContext.databaseAccount.name,
forwardingId: connectionInfo.data.forwardingId,
};
await this.phoenixClient.initiateContainerHeartBeat(containerData);
@ -1285,7 +1276,7 @@ export default class Explorer {
await useNotebook.getState().refreshNotebooksEnabledStateForAccount();
//Disable phoenix in case of Vnet or Firewall was enabled.
if (useNotebook.getState().isPhoenix && !isPublicInternetAccessAllowed()) {
if (!isPublicInternetAccessAllowed()) {
useNotebook.getState().setIsPhoenix(false);
}
// TODO: remove reference to isNotebookEnabled and isNotebooksEnabledForAccount
@ -1299,12 +1290,7 @@ export default class Explorer {
});
if (useNotebook.getState().isPhoenix) {
if (isNotebookEnabled) {
await this.initNotebooks(userContext.databaseAccount);
} else if (this.notebookToImport) {
// if notebooks is not enabled but the user is trying to do a quickstart setup with notebooks, open the SetupNotebooksPane
this._openSetupNotebooksPaneForQuickstart();
}
await this.initNotebooks(userContext.databaseAccount);
}
}
}

View File

@ -78,8 +78,9 @@ export function createStaticCommandBarButtons(
if (container.notebookManager?.gitHubOAuthService) {
notebookButtons.push(createManageGitHubAccountButton(container));
}
notebookButtons.push(createOpenTerminalButton(container));
if (useNotebook.getState().isPhoenix && configContext.IS_MPAC) {
notebookButtons.push(createOpenTerminalButton(container));
}
if (selectedNodeState.isConnectedToContainer()) {
notebookButtons.push(createNotebookWorkspaceResetButton(container));
}

View File

@ -8,15 +8,8 @@ import { ConnectionStatusType, HttpHeaders, HttpStatusCodes, Notebook } from "..
import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
import * as Logger from "../../Common/Logger";
import * as DataModels from "../../Contracts/DataModels";
import {
ContainerConnectionInfo,
IAccountData,
IPhoenixConnectionInfoResult,
IProvisionData,
IResponse,
} from "../../Contracts/DataModels";
import { IPhoenixConnectionInfoResult, IProvisionData, IResponse } from "../../Contracts/DataModels";
import { userContext } from "../../UserContext";
import { createOrUpdate, destroy } from "../../Utils/arm/generatedClients/cosmosNotebooks/notebookWorkspaces";
import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { useNotebook } from "./useNotebook";
@ -55,21 +48,11 @@ export class NotebookContainerClient {
*/
private scheduleHeartbeat(delayMs: number): void {
setTimeout(async () => {
try {
const memoryUsageInfo = await this.getMemoryUsage();
useNotebook.getState().setMemoryUsageInfo(memoryUsageInfo);
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
if (notebookServerInfo?.notebookServerEndpoint) {
this.scheduleHeartbeat(Constants.Notebook.heartbeatDelayMs);
}
} catch (exception) {
if (useNotebook.getState().isPhoenix) {
const connectionStatus: ContainerConnectionInfo = {
status: ConnectionStatusType.Failed,
};
useNotebook.getState().resetContainerConnection(connectionStatus);
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
}
const memoryUsageInfo = await this.getMemoryUsage();
useNotebook.getState().setMemoryUsageInfo(memoryUsageInfo);
const notebookServerInfo = useNotebook.getState().notebookServerInfo;
if (notebookServerInfo?.notebookServerEndpoint) {
this.scheduleHeartbeat(Constants.Notebook.heartbeatDelayMs);
}
}, delayMs);
}
@ -108,7 +91,7 @@ export class NotebookContainerClient {
notebookServerEndpoint: string,
authToken: string
): Promise<DataModels.MemoryUsageInfo> {
if (this.checkStatus()) {
if (this.shouldExecuteMemoryCall()) {
const response = await fetch(`${notebookServerEndpoint}api/metrics/memory`, {
method: "GET",
headers: {
@ -137,26 +120,14 @@ export class NotebookContainerClient {
}
}
private checkStatus(): boolean {
if (useNotebook.getState().isPhoenix) {
if (
useNotebook.getState().containerStatus?.status === Constants.ContainerStatusType.Disconnected &&
useNotebook.getState().connectionInfo?.status === ConnectionStatusType.Connect
) {
const connectionStatus: ContainerConnectionInfo = {
status: ConnectionStatusType.Reconnect,
};
useNotebook.getState().resetContainerConnection(connectionStatus);
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
return false;
} else if (
useNotebook.getState().containerStatus?.status === Constants.ContainerStatusType.Disconnected ||
useNotebook.getState().connectionInfo?.status === ConnectionStatusType.Reconnect
) {
return false;
}
private shouldExecuteMemoryCall(): boolean {
if (
useNotebook.getState().containerStatus?.status === Constants.ContainerStatusType.Active &&
useNotebook.getState().connectionInfo?.status === ConnectionStatusType.Connected
) {
return true;
}
return true;
return false;
}
public async resetWorkspace(): Promise<IResponse<IPhoenixConnectionInfoResult>> {
@ -185,19 +156,11 @@ export class NotebookContainerClient {
const provisionData: IProvisionData = {
cosmosEndpoint: userContext.databaseAccount.properties.documentEndpoint,
};
const accountData: IAccountData = {
subscriptionId: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
dbAccountName: userContext.databaseAccount.name,
};
return await this.phoenixClient.resetContainer(provisionData, accountData);
return await this.phoenixClient.resetContainer(provisionData);
}
return null;
} catch (error) {
Logger.logError(getErrorMessage(error), "NotebookContainerClient/resetWorkspace");
if (!useNotebook.getState().isPhoenix) {
await this.recreateNotebookWorkspaceAsync();
}
throw error;
}
}
@ -212,25 +175,6 @@ export class NotebookContainerClient {
};
}
private async recreateNotebookWorkspaceAsync(): Promise<void> {
const { databaseAccount } = userContext;
if (!databaseAccount?.id) {
throw new Error("DataExplorer not initialized");
}
try {
await destroy(userContext.subscriptionId, userContext.resourceGroup, userContext.databaseAccount.name, "default");
await createOrUpdate(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
"default"
);
} catch (error) {
Logger.logError(getErrorMessage(error), "NotebookContainerClient/recreateNotebookWorkspaceAsync");
return Promise.reject(error);
}
}
private getHeaders(): HeadersInit {
const authorizationHeader = getAuthorizationHeader();
return {

View File

@ -8,7 +8,7 @@ import { getErrorMessage } from "../../Common/ErrorHandlingUtils";
import * as Logger from "../../Common/Logger";
import { configContext } from "../../ConfigContext";
import * as DataModels from "../../Contracts/DataModels";
import { ContainerConnectionInfo, ContainerInfo, IAccountData } from "../../Contracts/DataModels";
import { ContainerConnectionInfo, ContainerInfo } from "../../Contracts/DataModels";
import { useTabs } from "../../hooks/useTabs";
import { IPinnedRepo } from "../../Juno/JunoClient";
import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstants";
@ -300,13 +300,7 @@ export const useNotebook: UseStore<NotebookState> = create((set, get) => ({
getPhoenixStatus: async () => {
if (get().isPhoenix === undefined) {
const phoenixClient = new PhoenixClient();
const accountData: IAccountData = {
subscriptionId: userContext.subscriptionId,
resourceGroup: userContext.resourceGroup,
dbAccountName: userContext.databaseAccount.name,
};
const isPhoenix =
userContext.features.phoenix === true && (await phoenixClient.IsDbAcountWhitelisted(accountData));
const isPhoenix = userContext.features.phoenix === true && (await phoenixClient.IsDbAcountWhitelisted());
set({ isPhoenix });
}
},

View File

@ -121,7 +121,7 @@ export const ResourceTree: React.FC<ResourceTreeProps> = ({ container }: Resourc
children: [],
};
if (useNotebook.getState().isPhoenix === false) {
if (!useNotebook.getState().isPhoenix) {
notebooksTree.children.push(buildNotebooksTemporarilyDownTree());
} else {
if (galleryContentRoot) {

View File

@ -6,7 +6,6 @@ import { configContext } from "../ConfigContext";
import {
ContainerConnectionInfo,
ContainerInfo,
IAccountData,
IContainerData,
IPhoenixConnectionInfoResult,
IProvisionData,
@ -24,30 +23,21 @@ export class PhoenixClient {
minTimeout: Notebook.retryAttemptDelayMs,
};
public async allocateContainer(
provisionData: IProvisionData,
accountData: IAccountData
): Promise<IResponse<IPhoenixConnectionInfoResult>> {
return this.executeContainerAssignmentOperation(provisionData, accountData, "allocate");
public async allocateContainer(provisionData: IProvisionData): Promise<IResponse<IPhoenixConnectionInfoResult>> {
return this.executeContainerAssignmentOperation(provisionData, "allocate");
}
public async resetContainer(
provisionData: IProvisionData,
accountData: IAccountData
): Promise<IResponse<IPhoenixConnectionInfoResult>> {
return this.executeContainerAssignmentOperation(provisionData, accountData, "reset");
public async resetContainer(provisionData: IProvisionData): Promise<IResponse<IPhoenixConnectionInfoResult>> {
return this.executeContainerAssignmentOperation(provisionData, "reset");
}
private async executeContainerAssignmentOperation(
provisionData: IProvisionData,
accountData: IAccountData,
operation: string
): Promise<IResponse<IPhoenixConnectionInfoResult>> {
try {
const response = await fetch(
`${this.getPhoenixContainerPoolingEndPoint()}/subscriptions/${accountData.subscriptionId}/resourceGroups/${
accountData.resourceGroup
}/providers/Microsoft.DocumentDB/databaseAccounts/${accountData.dbAccountName}/containerconnections`,
`${this.getPhoenixControlPlaneEndpoint()}${userContext.databaseAccount.id}/containerconnections`,
{
method: operation === "allocate" ? "POST" : "PATCH",
headers: PhoenixClient.getHeaders(),
@ -86,11 +76,7 @@ export class PhoenixClient {
try {
const runContainerStatusAsync = async () => {
const response = await window.fetch(
`${this.getPhoenixContainerPoolingEndPoint()}/subscriptions/${containerData.subscriptionId}/resourceGroups/${
containerData.resourceGroup
}/providers/Microsoft.DocumentDB/databaseAccounts/${containerData.dbAccountName}/${
containerData.forwardingId
}`,
`${this.getPhoenixControlPlaneEndpoint()}${userContext.databaseAccount.id}/${containerData.forwardingId}`,
{
method: "GET",
headers: PhoenixClient.getHeaders(),
@ -130,37 +116,19 @@ export class PhoenixClient {
}
private async getContainerHealth(delayMs: number, containerData: IContainerData) {
try {
const containerInfo = await this.getContainerStatusAsync(containerData);
useNotebook.getState().setContainerStatus(containerInfo);
if (useNotebook.getState().containerStatus?.status === ContainerStatusType.Active) {
this.scheduleContainerHeartbeat(delayMs, containerData);
}
} catch (exception) {
const connectionStatus: ContainerConnectionInfo = {
status: ConnectionStatusType.Reconnect,
};
useNotebook.getState().resetContainerConnection(connectionStatus);
useNotebook.getState().setIsRefreshed(!useNotebook.getState().isRefreshed);
useNotebook.getState().setContainerStatus({
durationLeftInMinutes: undefined,
notebookServerInfo: undefined,
status: ContainerStatusType.Disconnected,
});
const containerInfo = await this.getContainerStatusAsync(containerData);
useNotebook.getState().setContainerStatus(containerInfo);
if (useNotebook.getState().containerStatus?.status === ContainerStatusType.Active) {
this.scheduleContainerHeartbeat(delayMs, containerData);
}
}
public async IsDbAcountWhitelisted(accountData: IAccountData) {
public async IsDbAcountWhitelisted() {
try {
const response = await window.fetch(
`${this.getPhoenixContainerPoolingEndPoint()}/subscriptions/${accountData.subscriptionId}/resourceGroups/${
accountData.resourceGroup
}/providers/Microsoft.DocumentDB/databaseAccounts/${accountData.dbAccountName}`,
{
method: "GET",
headers: PhoenixClient.getHeaders(),
}
);
const response = await window.fetch(`${this.getPhoenixControlPlaneEndpoint()}${userContext.databaseAccount.id}`, {
method: "GET",
headers: PhoenixClient.getHeaders(),
});
return response.status === HttpStatusCodes.OK;
} catch (error) {
Logger.logError(getErrorMessage(error), "PhoenixClient/IsDbAcountWhitelisted");
@ -180,7 +148,7 @@ export class PhoenixClient {
return phoenixEndpoint;
}
public getPhoenixContainerPoolingEndPoint(): string {
public getPhoenixControlPlaneEndpoint(): string {
return `${PhoenixClient.getPhoenixEndpoint()}/api/controlplane/toolscontainer/cosmosaccounts`;
}