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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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