From 4d19610c945a965c00d3cce858d5eda164277d87 Mon Sep 17 00:00:00 2001 From: Kevin Cunnane Date: Tue, 27 Aug 2019 14:28:14 -0700 Subject: [PATCH] Fix #6845 Notebook: Pyspark3 links incorrectly mapped when AD enabled (#6885) --- .../parts/notebook/common/models/cell.ts | 38 ++++++------ .../notebook/common/models/notebookUtils.ts | 62 ++++++++++++++++++- 2 files changed, 79 insertions(+), 21 deletions(-) diff --git a/src/sql/workbench/parts/notebook/common/models/cell.ts b/src/sql/workbench/parts/notebook/common/models/cell.ts index 53f92a8fe4..452a4f6f77 100644 --- a/src/sql/workbench/parts/notebook/common/models/cell.ts +++ b/src/sql/workbench/parts/notebook/common/models/cell.ts @@ -24,6 +24,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; let modelId = 0; + export class CellModel implements ICellModel { private _cellType: nb.CellType; private _source: string | string[]; @@ -471,17 +472,20 @@ export class CellModel implements ICellModel { if (result && result.data && result.data['text/html']) { let model = (this as CellModel).options.notebook as NotebookModel; if (model.activeConnection) { - let endpoint = this.getKnoxEndpoint(model.activeConnection); - let host = endpoint && endpoint.ipAddress ? endpoint.ipAddress : model.activeConnection.serverName; - let port = endpoint && endpoint.port ? ':' + endpoint.port.toString() : defaultPort; - let html = result.data['text/html']; - // CTP 3.1 and earlier Spark link - html = this.rewriteUrlUsingRegex(/(https?:\/\/master.*\/proxy)(.*)/g, html, host, port, yarnUi); - // CTP 3.2 and later spark link - html = this.rewriteUrlUsingRegex(/(https?:\/\/sparkhead.*\/proxy)(.*)/g, html, host, port, yarnUi); - // Driver link - html = this.rewriteUrlUsingRegex(/(https?:\/\/storage.*\/containerlogs)(.*)/g, html, host, port, driverLog); - (output).data['text/html'] = html; + let gatewayEndpointInfo = this.getGatewayEndpoint(model.activeConnection); + if (gatewayEndpointInfo) { + let hostAndIp = notebookUtils.getHostAndPortFromEndpoint(gatewayEndpointInfo.endpoint); + let host = gatewayEndpointInfo && hostAndIp.host ? hostAndIp.host : model.activeConnection.serverName; + let port = gatewayEndpointInfo && hostAndIp.port ? ':' + hostAndIp.port : defaultPort; + let html = result.data['text/html']; + // CTP 3.1 and earlier Spark link + html = this.rewriteUrlUsingRegex(/(https?:\/\/master.*\/proxy)(.*)/g, html, host, port, yarnUi); + // CTP 3.2 and later spark link + html = this.rewriteUrlUsingRegex(/(https?:\/\/sparkhead.*\/proxy)(.*)/g, html, host, port, yarnUi); + // Driver link + html = this.rewriteUrlUsingRegex(/(https?:\/\/storage.*\/containerlogs)(.*)/g, html, host, port, driverLog); + (output).data['text/html'] = html; + } } } } @@ -601,23 +605,21 @@ export class CellModel implements ICellModel { // Get Knox endpoint from IConnectionProfile // TODO: this will be refactored out into the notebooks extension as a contribution point - private getKnoxEndpoint(activeConnection: IConnectionProfile): notebookUtils.IEndpoint { + private getGatewayEndpoint(activeConnection: IConnectionProfile): notebookUtils.IEndpoint { let endpoint; if (this._connectionManagementService && activeConnection && activeConnection.providerName.toLowerCase() === notebookConstants.SQL_CONNECTION_PROVIDER.toLowerCase()) { let serverInfo: ServerInfo = this._connectionManagementService.getServerInfo(activeConnection.id); - if (serverInfo && serverInfo.options && serverInfo.options['clusterEndpoints']) { - let endpoints: notebookUtils.IEndpoint[] = serverInfo.options['clusterEndpoints']; + if (serverInfo) { + let endpoints: notebookUtils.IEndpoint[] = notebookUtils.getClusterEndpoints(serverInfo); if (endpoints && endpoints.length > 0) { - endpoint = endpoints.find(ep => { - let serviceName: string = ep.serviceName.toLowerCase(); - return serviceName === 'knox' || serviceName === 'gateway'; - }); + endpoint = endpoints.find(ep => ep.serviceName.toLowerCase() === notebookUtils.hadoopEndpointNameGateway); } } } return endpoint; } + private getMultilineSource(source: string | string[]): string | string[] { if (typeof source === 'string') { let sourceMultiline = source.split('\n'); diff --git a/src/sql/workbench/parts/notebook/common/models/notebookUtils.ts b/src/sql/workbench/parts/notebook/common/models/notebookUtils.ts index eec7f2100b..c07f7df847 100644 --- a/src/sql/workbench/parts/notebook/common/models/notebookUtils.ts +++ b/src/sql/workbench/parts/notebook/common/models/notebookUtils.ts @@ -4,13 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import * as path from 'vs/base/common/path'; -import { nb } from 'azdata'; +import { nb, ServerInfo } from 'azdata'; import { DEFAULT_NOTEBOOK_PROVIDER, DEFAULT_NOTEBOOK_FILETYPE, INotebookService } from 'sql/workbench/services/notebook/common/notebookService'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICellModel } from 'sql/workbench/parts/notebook/common/models/modelInterfaces'; +import { URI } from 'vs/base/common/uri'; +export const clusterEndpointsProperty = 'clusterEndpoints'; +export const hadoopEndpointNameGateway = 'gateway'; /** * Test whether an output is from a stream. */ @@ -79,10 +82,12 @@ export interface IStandardKernelWithProvider { readonly notebookProvider: string; } + export interface IEndpoint { serviceName: string; - ipAddress: string; - port: number; + description: string; + endpoint: string; + protocol: string; } export function tryMatchCellMagic(input: string): string { @@ -125,3 +130,54 @@ export function convertVscodeResourceToFileInSubDirectories(htmlContent: string, export function useInProcMarkdown(configurationService: IConfigurationService): boolean { return configurationService.getValue('notebook.useInProcMarkdown'); } + +export function getClusterEndpoints(serverInfo: ServerInfo): IEndpoint[] | undefined { + let endpoints: RawEndpoint[] = serverInfo.options[clusterEndpointsProperty]; + if (!endpoints || endpoints.length === 0) { return []; } + + return endpoints.map(e => { + // If endpoint is missing, we're on CTP bits. All endpoints from the CTP serverInfo should be treated as HTTPS + let endpoint = e.endpoint ? e.endpoint : `https://${e.ipAddress}:${e.port}`; + let updatedEndpoint: IEndpoint = { + serviceName: e.serviceName, + description: e.description, + endpoint: endpoint, + protocol: e.protocol + }; + return updatedEndpoint; + }); +} + +export type HostAndIp = { host: string, port: string }; + +export function getHostAndPortFromEndpoint(endpoint: string): HostAndIp { + let authority = URI.parse(endpoint).authority; + let hostAndPortRegex = /^(.*)([,:](\d+))/g; + let match = hostAndPortRegex.exec(authority); + if (match) { + return { + host: match[1], + port: match[3] + }; + } + return { + host: authority, + port: undefined + }; +} + +interface RawEndpoint { + serviceName: string; + description?: string; + endpoint?: string; + protocol?: string; + ipAddress?: string; + port?: number; +} + +export interface IEndpoint { + serviceName: string; + description: string; + endpoint: string; + protocol: string; +}