mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d08d07fe39 | ||
|
|
7725d1f6a8 | ||
|
|
63803ea6f2 | ||
|
|
cacd481bff | ||
|
|
650ab3b800 | ||
|
|
517b375395 | ||
|
|
29ccbb5497 | ||
|
|
ba3e92e5be | ||
|
|
5013a76565 | ||
|
|
fe896e8b1a | ||
|
|
ea71c0e39c | ||
|
|
32acda343d | ||
|
|
e97e098a14 | ||
|
|
b317ccde28 | ||
|
|
3d1d20a472 | ||
|
|
7b2ae58aa6 | ||
|
|
ecedd3108d | ||
|
|
6d598b1362 | ||
|
|
1cae74c6d3 | ||
|
|
fbcd3c620c | ||
|
|
78ec3c016b | ||
|
|
71a6ced5bf | ||
|
|
eee799aeef | ||
|
|
3ffef2fb02 | ||
|
|
c7d7faa167 | ||
|
|
64e95edc96 | ||
|
|
761e3d1692 | ||
|
|
c1fb00b3f2 | ||
|
|
8c925a31b6 | ||
|
|
b196291052 | ||
|
|
068b64973f | ||
|
|
d83444ffeb | ||
|
|
872973c4d2 | ||
|
|
ca36aa5b2d | ||
|
|
0a7cdf79ba | ||
|
|
ccb7a03a0f | ||
|
|
beab3cb48c | ||
|
|
fd7b5a8628 | ||
|
|
fd0d3855df | ||
|
|
0498253b56 | ||
|
|
e5609ffd5c | ||
|
|
29e6145920 | ||
|
|
b03ff643e5 | ||
|
|
053be4756d | ||
|
|
2bdd55dea4 | ||
|
|
738c8231ee | ||
|
|
1e29949273 | ||
|
|
cdd3899648 | ||
|
|
933dfc0f3f | ||
|
|
10a7b47604 | ||
|
|
910dc32623 | ||
|
|
86f38f5074 | ||
|
|
ba9d3169c5 | ||
|
|
a19885e719 | ||
|
|
84457bf981 | ||
|
|
d73fa5e168 | ||
|
|
e9c3ffe424 | ||
|
|
a1aa303922 | ||
|
|
c9dd2fb4a8 | ||
|
|
3d95b0032e | ||
|
|
5ff747d6ef | ||
|
|
d26f4167fb | ||
|
|
93a28b30c7 | ||
|
|
10d7db8b37 | ||
|
|
c4a8b786c3 | ||
|
|
c99c09a6ab | ||
|
|
b6a36ebc59 | ||
|
|
b783a69e36 | ||
|
|
bbad8f194c | ||
|
|
6f6ba99251 | ||
|
|
f991293ee8 | ||
|
|
a60cd0132e | ||
|
|
05a53af085 |
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "Microsoft.sqlservernotebook",
|
||||
"version": "0.2.1",
|
||||
"version": "0.3.2",
|
||||
"repo": "https://github.com/Microsoft/azuredatastudio"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "Microsoft.sqlservernotebook",
|
||||
"version": "0.3.2",
|
||||
"repo": "https://github.com/Microsoft/azuredatastudio"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -24,6 +24,12 @@ const plumber = require('gulp-plumber');
|
||||
const ext = require('./lib/extensions');
|
||||
|
||||
const extensionsPath = path.join(path.dirname(__dirname), 'extensions');
|
||||
// {{SQL CARBON EDIT}}
|
||||
const sqlLocalizedExtensions = [
|
||||
'dacpac',
|
||||
'schema-compare'
|
||||
];
|
||||
// {{SQL CARBON EDIT}}
|
||||
|
||||
const compilations = glob.sync('**/tsconfig.json', {
|
||||
cwd: extensionsPath,
|
||||
@@ -52,10 +58,10 @@ const tasks = compilations.map(function (tsconfigFile) {
|
||||
let headerId, headerOut;
|
||||
let index = relativeDirname.indexOf('/');
|
||||
if (index < 0) {
|
||||
headerId = 'vscode.' + relativeDirname;
|
||||
headerId = 'microsoft.' + relativeDirname; // {{SQL CARBON EDIT}}
|
||||
headerOut = 'out';
|
||||
} else {
|
||||
headerId = 'vscode.' + relativeDirname.substr(0, index);
|
||||
headerId = 'microsoft.' + relativeDirname.substr(0, index); // {{SQL CARBON EDIT}}
|
||||
headerOut = relativeDirname.substr(index + 1) + '/out';
|
||||
}
|
||||
|
||||
@@ -108,7 +114,7 @@ const tasks = compilations.map(function (tsconfigFile) {
|
||||
const cleanTask = task.define(`clean-extension-${name}`, util.rimraf(out));
|
||||
|
||||
const compileTask = task.define(`compile-extension:${name}`, task.series(cleanTask, () => {
|
||||
const pipeline = createPipeline(false, true);
|
||||
const pipeline = createPipeline(sqlLocalizedExtensions.includes(name), true); // {{SQL CARBON EDIT}}
|
||||
const input = pipeline.tsProjectSrc();
|
||||
|
||||
return input
|
||||
|
||||
@@ -19,7 +19,8 @@ const ansiColors = require('ansi-colors');
|
||||
|
||||
const root = path.dirname(path.dirname(__dirname));
|
||||
// {{SQL CARBON EDIT}}
|
||||
const builtInExtensions = require('../builtInExtensions-insiders.json');
|
||||
const quality = process.env['VSCODE_QUALITY'];
|
||||
const builtInExtensions = quality && quality === 'stable' ? require('../builtInExtensions.json') : require('../builtInExtensions-insiders.json');
|
||||
// {{SQL CARBON EDIT}} - END
|
||||
const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json');
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ const sqlBuiltInExtensions = [
|
||||
'schema-compare',
|
||||
'cms',
|
||||
'query-history',
|
||||
'resource-deployment'
|
||||
'liveshare'
|
||||
];
|
||||
const builtInExtensions = process.env['VSCODE_QUALITY'] === 'stable' ? require('../builtInExtensions.json') : require('../builtInExtensions-insiders.json');
|
||||
// {{SQL CARBON EDIT}} - End
|
||||
|
||||
@@ -230,7 +230,6 @@ const sqlBuiltInExtensions = [
|
||||
// the extension will be excluded from SQLOps package and will have separate vsix packages
|
||||
'admin-tool-ext-win',
|
||||
'agent',
|
||||
'big-data-cluster',
|
||||
'import',
|
||||
'profiler',
|
||||
'admin-pack',
|
||||
@@ -238,7 +237,7 @@ const sqlBuiltInExtensions = [
|
||||
'schema-compare',
|
||||
'cms',
|
||||
'query-history',
|
||||
'resource-deployment'
|
||||
'liveshare'
|
||||
];
|
||||
|
||||
interface IBuiltInExtension {
|
||||
|
||||
@@ -20,8 +20,8 @@ Compression=lzma
|
||||
SolidCompression=yes
|
||||
AppMutex={code:GetAppMutex}
|
||||
SetupMutex={#AppMutex}setup
|
||||
WizardImageFile="{#RepoDir}\resources\win32\inno-big-100.bmp,{#RepoDir}\resources\win32\inno-big-125.bmp,{#RepoDir}\resources\win32\inno-big-150.bmp,{#RepoDir}\resources\win32\inno-big-175.bmp,{#RepoDir}\resources\win32\inno-big-200.bmp,{#RepoDir}\resources\win32\inno-big-225.bmp,{#RepoDir}\resources\win32\inno-big-250.bmp"
|
||||
WizardSmallImageFile="{#RepoDir}\resources\win32\inno-small-100.bmp,{#RepoDir}\resources\win32\inno-small-125.bmp,{#RepoDir}\resources\win32\inno-small-150.bmp,{#RepoDir}\resources\win32\inno-small-175.bmp,{#RepoDir}\resources\win32\inno-small-200.bmp,{#RepoDir}\resources\win32\inno-small-225.bmp,{#RepoDir}\resources\win32\inno-small-250.bmp"
|
||||
WizardImageFile="{#RepoDir}\resources\win32\sql-big.bmp"
|
||||
WizardSmallImageFile="{#RepoDir}\resources\win32\sql-small.bmp"
|
||||
SetupIconFile={#RepoDir}\resources\win32\code.ico
|
||||
UninstallDisplayIcon={app}\{#ExeBasename}.exe
|
||||
ChangesEnvironment=true
|
||||
|
||||
@@ -50,17 +50,17 @@
|
||||
"objectExplorer/item/context": [
|
||||
{
|
||||
"command": "adminToolExtWin.launchSsmsMinGswDialog",
|
||||
"when": "isWindows && connectionProvider == MSSQL && nodeType && nodeType == Database",
|
||||
"when": "isWindows && connectionProvider == MSSQL && nodeType && nodeType == Database && mssql:engineedition != 11",
|
||||
"group": "z-AdminToolExt@1"
|
||||
},
|
||||
{
|
||||
"command": "adminToolExtWin.launchSsmsMinPropertiesDialog",
|
||||
"when": "isWindows && connectionProvider == MSSQL && serverInfo && !isCloud && nodeType && nodeType == Server",
|
||||
"when": "isWindows && connectionProvider == MSSQL && serverInfo && !isCloud && nodeType && nodeType == Server && mssql:engineedition != 11",
|
||||
"group": "z-AdminToolExt@2"
|
||||
},
|
||||
{
|
||||
"command": "adminToolExtWin.launchSsmsMinPropertiesDialog",
|
||||
"when": "isWindows && connectionProvider == MSSQL && serverInfo && nodeType && nodeType =~ /^(Database|Table|Column|Index|Statistic|View|ServerLevelLogin|ServerLevelServerRole|ServerLevelCredential|ServerLevelServerAudit|ServerLevelServerAuditSpecification|StoredProcedure|ScalarValuedFunction|TableValuedFunction|AggregateFunction|Synonym|Assembly|UserDefinedDataType|UserDefinedType|UserDefinedTableType|Sequence|User|DatabaseRole|ApplicationRole|Schema|SecurityPolicy|ServerLevelLinkedServer)$/",
|
||||
"when": "isWindows && connectionProvider == MSSQL && serverInfo && nodeType && mssql:engineedition != 11 && nodeType =~ /^(Database|Table|Column|Index|Statistic|View|ServerLevelLogin|ServerLevelServerRole|ServerLevelCredential|ServerLevelServerAudit|ServerLevelServerAuditSpecification|StoredProcedure|ScalarValuedFunction|TableValuedFunction|AggregateFunction|Synonym|Assembly|UserDefinedDataType|UserDefinedType|UserDefinedTableType|Sequence|User|DatabaseRole|ApplicationRole|Schema|SecurityPolicy|ServerLevelLinkedServer)$/",
|
||||
"group": "z-AdminToolExt@2"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "agent",
|
||||
"displayName": "SQL Server Agent",
|
||||
"description": "Manage and troubleshoot SQL Server Agent jobs",
|
||||
"version": "0.43.0",
|
||||
"version": "0.44.0",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
|
||||
@@ -32,7 +32,7 @@
|
||||
"description": "Manage and troubleshoot SQL Agent jobs",
|
||||
"provider": "MSSQL",
|
||||
"title": "SQL Agent",
|
||||
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud",
|
||||
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud && mssql:engineedition != 11",
|
||||
"container": {
|
||||
"controlhost-container": {
|
||||
"type": "agent"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16"><defs><style>.cls-1,.cls-2{clip-rule:evenodd;}.cls-2,.cls-5,.cls-7{fill:#fff;}.cls-3,.cls-5{fill-rule:evenodd;}.cls-4{clip-path:url(#clip-path);}.cls-6{clip-path:url(#clip-path-2);}</style><clipPath id="clip-path"><path class="cls-1" d="M11.5-15.92v3.5a3.4,3.4,0,0,1-.23,1.24,3.48,3.48,0,0,1-.63,1.05,3.57,3.57,0,0,1-1,.77A3.43,3.43,0,0,1,8.5-9v6h-1V-9a3.39,3.39,0,0,1-1.2-.4,3.59,3.59,0,0,1-.95-.77,3.48,3.48,0,0,1-.63-1.05,3.4,3.4,0,0,1-.23-1.24v-3.5h1v-3h1v3h3v-3h1v3Zm-1,1h-5v2.5a2.45,2.45,0,0,0,.2,1,2.53,2.53,0,0,0,.53.8,2.53,2.53,0,0,0,.8.53,2.45,2.45,0,0,0,1,.2,2.42,2.42,0,0,0,1-.2,2.53,2.53,0,0,0,.79-.53,2.59,2.59,0,0,0,.54-.8,2.41,2.41,0,0,0,.2-1Z"/></clipPath><clipPath id="clip-path-2"><path class="cls-2" d="M11.5,3V6.5a3.4,3.4,0,0,1-.23,1.24,3.48,3.48,0,0,1-.63,1.05,3.57,3.57,0,0,1-1,.77A3.43,3.43,0,0,1,8.5,10v6h-1V10a3.39,3.39,0,0,1-1.2-.4,3.59,3.59,0,0,1-.95-.77,3.48,3.48,0,0,1-.63-1.05A3.4,3.4,0,0,1,4.5,6.5V3h1V0h1V3h3V0h1V3Zm-1,1h-5V6.5a2.45,2.45,0,0,0,.2,1A2.49,2.49,0,0,0,7,8.8,2.45,2.45,0,0,0,8,9a2.42,2.42,0,0,0,1-.2,2.53,2.53,0,0,0,.79-.53,2.59,2.59,0,0,0,.54-.8,2.41,2.41,0,0,0,.2-1Z"/></clipPath></defs><title>connect_to_inverse</title><path class="cls-3" d="M11.5-15.92v3.5a3.4,3.4,0,0,1-.23,1.24,3.48,3.48,0,0,1-.63,1.05,3.57,3.57,0,0,1-1,.77A3.43,3.43,0,0,1,8.5-9v6h-1V-9a3.39,3.39,0,0,1-1.2-.4,3.59,3.59,0,0,1-.95-.77,3.48,3.48,0,0,1-.63-1.05,3.4,3.4,0,0,1-.23-1.24v-3.5h1v-3h1v3h3v-3h1v3Zm-1,1h-5v2.5a2.45,2.45,0,0,0,.2,1,2.53,2.53,0,0,0,.53.8,2.53,2.53,0,0,0,.8.53,2.45,2.45,0,0,0,1,.2,2.42,2.42,0,0,0,1-.2,2.53,2.53,0,0,0,.79-.53,2.59,2.59,0,0,0,.54-.8,2.41,2.41,0,0,0,.2-1Z"/><g class="cls-4"><rect x="-0.5" y="-23.92" width="17" height="26"/></g><path class="cls-5" d="M11.5,3V6.5a3.4,3.4,0,0,1-.23,1.24,3.48,3.48,0,0,1-.63,1.05,3.57,3.57,0,0,1-1,.77A3.43,3.43,0,0,1,8.5,10v6h-1V10a3.39,3.39,0,0,1-1.2-.4,3.59,3.59,0,0,1-.95-.77,3.48,3.48,0,0,1-.63-1.05A3.4,3.4,0,0,1,4.5,6.5V3h1V0h1V3h3V0h1V3Zm-1,1h-5V6.5a2.45,2.45,0,0,0,.2,1A2.49,2.49,0,0,0,7,8.8,2.45,2.45,0,0,0,8,9a2.42,2.42,0,0,0,1-.2,2.53,2.53,0,0,0,.79-.53,2.59,2.59,0,0,0,.54-.8,2.41,2.41,0,0,0,.2-1Z"/><g class="cls-6"><rect class="cls-7" x="-0.5" y="-5" width="17" height="26"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
1
extensions/azurecore/resources/light/connect_to.svg
Normal file
1
extensions/azurecore/resources/light/connect_to.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16"><defs><style>.cls-1,.cls-2{clip-rule:evenodd;}.cls-2,.cls-5,.cls-7{fill:#fff;}.cls-3,.cls-5{fill-rule:evenodd;}.cls-4{clip-path:url(#clip-path);}.cls-6{clip-path:url(#clip-path-2);}</style><clipPath id="clip-path"><path class="cls-1" d="M11.5,3V6.5a3.4,3.4,0,0,1-.23,1.24,3.48,3.48,0,0,1-.63,1.05,3.57,3.57,0,0,1-1,.77A3.43,3.43,0,0,1,8.5,10v6h-1V10a3.39,3.39,0,0,1-1.2-.4,3.59,3.59,0,0,1-.95-.77,3.48,3.48,0,0,1-.63-1.05A3.4,3.4,0,0,1,4.5,6.5V3h1V0h1V3h3V0h1V3Zm-1,1h-5V6.5a2.45,2.45,0,0,0,.2,1A2.49,2.49,0,0,0,7,8.8,2.45,2.45,0,0,0,8,9a2.42,2.42,0,0,0,1-.2,2.53,2.53,0,0,0,.79-.53,2.59,2.59,0,0,0,.54-.8,2.41,2.41,0,0,0,.2-1Z"/></clipPath><clipPath id="clip-path-2"><path class="cls-2" d="M11.5,21.92v3.5a3.4,3.4,0,0,1-.23,1.24,3.48,3.48,0,0,1-.63,1.05,3.57,3.57,0,0,1-1,.77,3.43,3.43,0,0,1-1.19.4v6h-1v-6a3.39,3.39,0,0,1-1.2-.4,3.59,3.59,0,0,1-.95-.77,3.48,3.48,0,0,1-.63-1.05,3.4,3.4,0,0,1-.23-1.24v-3.5h1v-3h1v3h3v-3h1v3Zm-1,1h-5v2.5a2.45,2.45,0,0,0,.2,1A2.49,2.49,0,0,0,7,27.72a2.45,2.45,0,0,0,1,.2,2.42,2.42,0,0,0,1-.2,2.53,2.53,0,0,0,.79-.53,2.59,2.59,0,0,0,.54-.8,2.41,2.41,0,0,0,.2-1Z"/></clipPath></defs><title>connect_to</title><path class="cls-3" d="M11.5,3V6.5a3.4,3.4,0,0,1-.23,1.24,3.48,3.48,0,0,1-.63,1.05,3.57,3.57,0,0,1-1,.77A3.43,3.43,0,0,1,8.5,10v6h-1V10a3.39,3.39,0,0,1-1.2-.4,3.59,3.59,0,0,1-.95-.77,3.48,3.48,0,0,1-.63-1.05A3.4,3.4,0,0,1,4.5,6.5V3h1V0h1V3h3V0h1V3Zm-1,1h-5V6.5a2.45,2.45,0,0,0,.2,1A2.49,2.49,0,0,0,7,8.8,2.45,2.45,0,0,0,8,9a2.42,2.42,0,0,0,1-.2,2.53,2.53,0,0,0,.79-.53,2.59,2.59,0,0,0,.54-.8,2.41,2.41,0,0,0,.2-1Z"/><g class="cls-4"><rect x="-0.5" y="-5" width="17" height="26"/></g><path class="cls-5" d="M11.5,21.92v3.5a3.4,3.4,0,0,1-.23,1.24,3.48,3.48,0,0,1-.63,1.05,3.57,3.57,0,0,1-1,.77,3.43,3.43,0,0,1-1.19.4v6h-1v-6a3.39,3.39,0,0,1-1.2-.4,3.59,3.59,0,0,1-.95-.77,3.48,3.48,0,0,1-.63-1.05,3.4,3.4,0,0,1-.23-1.24v-3.5h1v-3h1v3h3v-3h1v3Zm-1,1h-5v2.5a2.45,2.45,0,0,0,.2,1A2.49,2.49,0,0,0,7,27.72a2.45,2.45,0,0,0,1,.2,2.42,2.42,0,0,0,1-.2,2.53,2.53,0,0,0,.79-.53,2.59,2.59,0,0,0,.54-.8,2.41,2.41,0,0,0,.2-1Z"/><g class="cls-6"><rect class="cls-7" x="-0.5" y="13.92" width="17" height="26"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -139,7 +139,18 @@
|
||||
"command": "bigDataClusters.command.deletemount",
|
||||
"title": "%command.deletemount.title%"
|
||||
}
|
||||
]
|
||||
],
|
||||
"configuration": {
|
||||
"type": "object",
|
||||
"title": "%bdc.configuration.title%",
|
||||
"properties": {
|
||||
"bigDataCluster.ignoreSslVerification": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "%bdc.ignoreSslVerification.desc%"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"kerberos": "^1.1.3",
|
||||
|
||||
@@ -7,5 +7,7 @@
|
||||
"command.manageController.title" : "Manage",
|
||||
"command.mount.title" : "Mount HDFS",
|
||||
"command.refreshmount.title" : "Refresh Mount",
|
||||
"command.deletemount.title" : "Delete Mount"
|
||||
"command.deletemount.title" : "Delete Mount",
|
||||
"bdc.configuration.title" : "Big Data Cluster",
|
||||
"bdc.ignoreSslVerification.desc" : "Ignore SSL verification errors against SQL Server Big Data Cluster endpoints such as HDFS, Spark, and Controller if true"
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export enum BdcItemType {
|
||||
@@ -63,6 +61,7 @@ export namespace cssStyles {
|
||||
export const tableHeader = { 'font-weight': 'bold', 'text-transform': 'uppercase', 'font-size': '10px', 'user-select': 'text' };
|
||||
export const hyperlink = { 'user-select': 'text', 'color': '#0078d4', 'text-decoration': 'underline', 'cursor': 'pointer' };
|
||||
export const text = { 'margin-block-start': '0px', 'margin-block-end': '0px' };
|
||||
export const overflowEllipsisText = { ...text, 'overflow': 'hidden', 'text-overflow': 'ellipsis' };
|
||||
export const nonSelectableText = { ...cssStyles.text, 'user-select': 'none' };
|
||||
export const tabHeaderText = { 'margin-block-start': '2px', 'margin-block-end': '0px', 'user-select': 'none' };
|
||||
export const selectedResourceHeaderTab = { 'font-weight': 'bold', 'color': '' };
|
||||
@@ -70,6 +69,7 @@ export namespace cssStyles {
|
||||
export const selectedTabDiv = { 'border-bottom': '2px solid #000' };
|
||||
export const unselectedTabDiv = { 'border-bottom': '1px solid #ccc' };
|
||||
export const lastUpdatedText = { ...text, 'color': '#595959' };
|
||||
export const errorText = { ...text, 'color': 'red' };
|
||||
}
|
||||
|
||||
export type AuthType = 'integrated' | 'basic';
|
||||
|
||||
@@ -4,32 +4,30 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as request from 'request';
|
||||
|
||||
import { authenticateKerberos, getHostAndPortFromEndpoint } from '../auth';
|
||||
import { BdcRouterApi, Authentication, EndpointModel, BdcStatusModel, DefaultApi } from './apiGenerated';
|
||||
import { TokenRouterApi } from './clusterApiGenerated2';
|
||||
import { AuthType } from '../constants';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { ConnectControllerDialog, ConnectControllerModel } from '../dialog/connectControllerDialog';
|
||||
import { getIgnoreSslVerificationConfigSetting } from '../utils';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
class SslAuth implements Authentication {
|
||||
|
||||
constructor(private _ignoreSslVerification: boolean) {
|
||||
}
|
||||
constructor() { }
|
||||
|
||||
applyToRequest(requestOptions: request.Options): void {
|
||||
requestOptions['agentOptions'] = {
|
||||
rejectUnauthorized: !this._ignoreSslVerification
|
||||
rejectUnauthorized: !getIgnoreSslVerificationConfigSetting()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class KerberosAuth extends SslAuth implements Authentication {
|
||||
|
||||
constructor(public kerberosToken: string, ignoreSslVerification: boolean) {
|
||||
super(ignoreSslVerification);
|
||||
constructor(public kerberosToken: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
applyToRequest(requestOptions: request.Options): void {
|
||||
@@ -41,8 +39,8 @@ export class KerberosAuth extends SslAuth implements Authentication {
|
||||
}
|
||||
}
|
||||
export class BasicAuth extends SslAuth implements Authentication {
|
||||
constructor(public username: string, public password: string, ignoreSslVerification: boolean) {
|
||||
super(ignoreSslVerification);
|
||||
constructor(public username: string, public password: string) {
|
||||
super();
|
||||
}
|
||||
|
||||
applyToRequest(requestOptions: request.Options): void {
|
||||
@@ -88,37 +86,52 @@ class DefaultApiWrapper extends DefaultApi {
|
||||
|
||||
export class ClusterController {
|
||||
|
||||
private authPromise: Promise<Authentication>;
|
||||
private _authPromise: Promise<Authentication>;
|
||||
private _url: string;
|
||||
private readonly dialog: ConnectControllerDialog;
|
||||
private connectionPromise: Promise<ClusterController>;
|
||||
private readonly _dialog: ConnectControllerDialog;
|
||||
private _connectionPromise: Promise<ClusterController>;
|
||||
|
||||
constructor(url: string,
|
||||
private authType: AuthType,
|
||||
private username?: string,
|
||||
private password?: string,
|
||||
ignoreSslVerification?: boolean
|
||||
private _authType: AuthType,
|
||||
private _username?: string,
|
||||
private _password?: string
|
||||
) {
|
||||
if (!url || (authType === 'basic' && (!username || !password))) {
|
||||
if (!url || (_authType === 'basic' && (!_username || !_password))) {
|
||||
throw new Error('Missing required inputs for Cluster controller API (URL, username, password)');
|
||||
}
|
||||
this._url = adjustUrl(url);
|
||||
if (this.authType === 'basic') {
|
||||
this.authPromise = Promise.resolve(new BasicAuth(username, password, !!ignoreSslVerification));
|
||||
if (this._authType === 'basic') {
|
||||
this._authPromise = Promise.resolve(new BasicAuth(_username, _password));
|
||||
} else {
|
||||
this.authPromise = this.requestTokenUsingKerberos(ignoreSslVerification);
|
||||
this._authPromise = this.requestTokenUsingKerberos();
|
||||
}
|
||||
this.dialog = new ConnectControllerDialog(new ConnectControllerModel(
|
||||
this._dialog = new ConnectControllerDialog(new ConnectControllerModel(
|
||||
{
|
||||
url: this._url,
|
||||
auth: this.authType,
|
||||
username: this.username,
|
||||
password: this.password
|
||||
auth: this._authType,
|
||||
username: this._username,
|
||||
password: this._password
|
||||
}));
|
||||
}
|
||||
|
||||
private async requestTokenUsingKerberos(ignoreSslVerification?: boolean): Promise<Authentication> {
|
||||
let supportsKerberos = await this.verifyKerberosSupported(ignoreSslVerification);
|
||||
public get url(): string {
|
||||
return this._url;
|
||||
}
|
||||
|
||||
public get authType(): AuthType {
|
||||
return this._authType;
|
||||
}
|
||||
|
||||
public get username(): string | undefined {
|
||||
return this._username;
|
||||
}
|
||||
|
||||
public get password(): string | undefined {
|
||||
return this._password;
|
||||
}
|
||||
|
||||
private async requestTokenUsingKerberos(): Promise<Authentication> {
|
||||
let supportsKerberos = await this.verifyKerberosSupported();
|
||||
if (!supportsKerberos) {
|
||||
throw new Error(localize('error.no.activedirectory', "This cluster does not support Windows authentication"));
|
||||
}
|
||||
@@ -129,9 +142,9 @@ export class ClusterController {
|
||||
let host = getHostAndPortFromEndpoint(this._url).host;
|
||||
let kerberosToken = await authenticateKerberos(host);
|
||||
let tokenApi = new TokenRouterApi(this._url);
|
||||
tokenApi.setDefaultAuthentication(new KerberosAuth(kerberosToken, !!ignoreSslVerification));
|
||||
tokenApi.setDefaultAuthentication(new KerberosAuth(kerberosToken));
|
||||
let result = await tokenApi.apiV1TokenPost();
|
||||
let auth = new OAuthWithSsl(ignoreSslVerification);
|
||||
let auth = new OAuthWithSsl();
|
||||
auth.accessToken = result.body.accessToken;
|
||||
return auth;
|
||||
} catch (error) {
|
||||
@@ -144,9 +157,9 @@ export class ClusterController {
|
||||
}
|
||||
}
|
||||
|
||||
private async verifyKerberosSupported(ignoreSslVerification: boolean): Promise<boolean> {
|
||||
private async verifyKerberosSupported(): Promise<boolean> {
|
||||
let tokenApi = new TokenRouterApi(this._url);
|
||||
tokenApi.setDefaultAuthentication(new SslAuth(!!ignoreSslVerification));
|
||||
tokenApi.setDefaultAuthentication(new SslAuth());
|
||||
try {
|
||||
await tokenApi.apiV1TokenPost();
|
||||
// If we get to here, the route for endpoints doesn't require auth so state this is false
|
||||
@@ -166,8 +179,8 @@ export class ClusterController {
|
||||
}
|
||||
|
||||
private async getEndpointsImpl(self: ClusterController): Promise<IEndPointsResponse> {
|
||||
let auth = await self.authPromise;
|
||||
let endPointApi = new BdcApiWrapper(self.username, self.password, self._url, auth);
|
||||
let auth = await self._authPromise;
|
||||
let endPointApi = new BdcApiWrapper(self._username, self._password, self._url, auth);
|
||||
let options: any = {};
|
||||
|
||||
let result = await endPointApi.endpointsGet(options);
|
||||
@@ -185,8 +198,8 @@ export class ClusterController {
|
||||
}
|
||||
|
||||
private async getBdcStatusImpl(self: ClusterController): Promise<IBdcStatusResponse> {
|
||||
let auth = await self.authPromise;
|
||||
const bdcApi = new BdcApiWrapper(self.username, self.password, self._url, auth);
|
||||
let auth = await self._authPromise;
|
||||
const bdcApi = new BdcApiWrapper(self._username, self._password, self._url, auth);
|
||||
|
||||
const bdcStatus = await bdcApi.getBdcStatus('', '', /*all*/ true);
|
||||
return {
|
||||
@@ -206,8 +219,8 @@ export class ClusterController {
|
||||
}
|
||||
|
||||
private async mountHdfsImpl(self: ClusterController, mountPath: string, remoteUri: string, credentials: {}): Promise<MountResponse> {
|
||||
let auth = await self.authPromise;
|
||||
const api = new DefaultApiWrapper(self.username, self.password, self._url, auth);
|
||||
let auth = await self._authPromise;
|
||||
const api = new DefaultApiWrapper(self._username, self._password, self._url, auth);
|
||||
|
||||
const mountStatus = await api.createMount('', '', remoteUri, mountPath, credentials);
|
||||
return {
|
||||
@@ -225,8 +238,8 @@ export class ClusterController {
|
||||
}
|
||||
|
||||
private async getMountStatusImpl(self: ClusterController, mountPath?: string): Promise<MountStatusResponse> {
|
||||
const auth = await self.authPromise;
|
||||
const api = new DefaultApiWrapper(self.username, self.password, self._url, auth);
|
||||
const auth = await self._authPromise;
|
||||
const api = new DefaultApiWrapper(self._username, self._password, self._url, auth);
|
||||
|
||||
const mountStatus = await api.listMounts('', '', mountPath);
|
||||
return {
|
||||
@@ -244,8 +257,8 @@ export class ClusterController {
|
||||
}
|
||||
|
||||
private async refreshMountImpl(self: ClusterController, mountPath: string): Promise<MountResponse> {
|
||||
const auth = await self.authPromise;
|
||||
const api = new DefaultApiWrapper(self.username, self.password, self._url, auth);
|
||||
const auth = await self._authPromise;
|
||||
const api = new DefaultApiWrapper(self._username, self._password, self._url, auth);
|
||||
|
||||
const mountStatus = await api.refreshMount('', '', mountPath);
|
||||
return {
|
||||
@@ -263,8 +276,8 @@ export class ClusterController {
|
||||
}
|
||||
|
||||
private async deleteMountImpl(mountPath: string): Promise<MountResponse> {
|
||||
let auth = await this.authPromise;
|
||||
const api = new DefaultApiWrapper(this.username, this.password, this._url, auth);
|
||||
let auth = await this._authPromise;
|
||||
const api = new DefaultApiWrapper(this._username, this._password, this._url, auth);
|
||||
|
||||
const mountStatus = await api.deleteMount('', '', mountPath);
|
||||
return {
|
||||
@@ -285,23 +298,23 @@ export class ClusterController {
|
||||
private async withConnectRetry<T>(f: (...args: any[]) => Promise<T>, promptConnect: boolean, errorMessage: string, ...args: any[]): Promise<T> {
|
||||
try {
|
||||
try {
|
||||
return await f(this, args);
|
||||
return await f(this, ...args);
|
||||
} catch (error) {
|
||||
if (promptConnect) {
|
||||
// We don't want to open multiple dialogs here if multiple calls come in the same time so check
|
||||
// and see if we have are actively waiting on an open dialog to return and if so then just wait
|
||||
// on that promise.
|
||||
if (!this.connectionPromise) {
|
||||
this.connectionPromise = this.dialog.showDialog();
|
||||
if (!this._connectionPromise) {
|
||||
this._connectionPromise = this._dialog.showDialog();
|
||||
}
|
||||
const controller = await this.connectionPromise;
|
||||
this.connectionPromise = undefined;
|
||||
const controller = await this._connectionPromise;
|
||||
this._connectionPromise = undefined;
|
||||
if (controller) {
|
||||
this.username = controller.username;
|
||||
this.password = controller.password;
|
||||
this._username = controller._username;
|
||||
this._password = controller._password;
|
||||
this._url = controller._url;
|
||||
this.authType = controller.authType;
|
||||
this.authPromise = controller.authPromise;
|
||||
this._authType = controller._authType;
|
||||
this._authPromise = controller._authPromise;
|
||||
}
|
||||
return await f(this, args);
|
||||
}
|
||||
@@ -378,7 +391,7 @@ export class ControllerError extends Error {
|
||||
public code?: number;
|
||||
public reason?: string;
|
||||
public address?: string;
|
||||
|
||||
public statusMessage?: string;
|
||||
/**
|
||||
*
|
||||
* @param error The original error to wrap
|
||||
@@ -391,6 +404,7 @@ export class ControllerError extends Error {
|
||||
this.code = error.response.statusCode;
|
||||
this.message += `${error.response.statusMessage ? ` - ${error.response.statusMessage}` : ''}` || '';
|
||||
this.address = error.response.url || '';
|
||||
this.statusMessage = error.response.statusMessage;
|
||||
}
|
||||
else if (error.message) {
|
||||
this.message += ` - ${error.message}`;
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
'use strict';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { ClusterController, ControllerError } from '../controller/clusterControllerApi';
|
||||
import { ControllerTreeDataProvider } from '../tree/controllerTreeDataProvider';
|
||||
import { TreeNode } from '../tree/treeNode';
|
||||
import { AuthType } from '../constants';
|
||||
import { ManageControllerCommand } from '../../extension';
|
||||
import { BdcDashboardOptions } from './bdcDashboardModel';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -67,13 +70,14 @@ export class AddControllerDialogModel {
|
||||
}
|
||||
}
|
||||
// We pre-fetch the endpoints here to verify that the information entered is correct (the user is able to connect)
|
||||
let controller = new ClusterController(url, auth, username, password, true);
|
||||
let controller = new ClusterController(url, auth, username, password);
|
||||
let response = await controller.getEndPoints();
|
||||
if (response && response.endPoints) {
|
||||
if (this._canceled) {
|
||||
return;
|
||||
}
|
||||
this.treeDataProvider.addController(url, auth, username, password, rememberPassword);
|
||||
this.treeDataProvider.addOrUpdateController(url, auth, username, password, rememberPassword);
|
||||
vscode.commands.executeCommand(ManageControllerCommand, <BdcDashboardOptions>{ url: url, auth: auth, username: username, password: password });
|
||||
await this.treeDataProvider.saveControllers();
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { BdcDashboardModel } from './bdcDashboardModel';
|
||||
import { BdcDashboardModel, getTroubleshootNotebookUrl, BdcErrorEvent } from './bdcDashboardModel';
|
||||
import { IconPathHelper, cssStyles } from '../constants';
|
||||
import { BdcServiceStatusPage } from './bdcServiceStatusPage';
|
||||
import { BdcDashboardOverviewPage } from './bdcDashboardOverviewPage';
|
||||
import { BdcStatusModel, ServiceStatusModel } from '../controller/apiGenerated';
|
||||
import { getHealthStatusDot, getServiceNameDisplayText } from '../utils';
|
||||
import { getHealthStatusDot, getServiceNameDisplayText, showErrorMessage } from '../utils';
|
||||
import { HdfsDialogCancelledError } from './hdfsDialogBase';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -22,7 +23,7 @@ const navWidth = '200px';
|
||||
const selectedTabCss = { 'font-weight': 'bold' };
|
||||
const unselectedTabCss = { 'font-weight': '' };
|
||||
|
||||
type NavTab = { div: azdata.DivContainer, dot: azdata.TextComponent, text: azdata.TextComponent };
|
||||
type NavTab = { serviceName: string, div: azdata.DivContainer, dot: azdata.TextComponent, text: azdata.TextComponent };
|
||||
|
||||
export class BdcDashboard {
|
||||
|
||||
@@ -33,6 +34,7 @@ export class BdcDashboard {
|
||||
private modelView: azdata.ModelView;
|
||||
private mainAreaContainer: azdata.FlexContainer;
|
||||
private navContainer: azdata.FlexContainer;
|
||||
private overviewPage: BdcDashboardOverviewPage;
|
||||
|
||||
private currentTab: NavTab;
|
||||
private currentPage: azdata.FlexContainer;
|
||||
@@ -43,6 +45,7 @@ export class BdcDashboard {
|
||||
|
||||
constructor(private title: string, private model: BdcDashboardModel) {
|
||||
this.model.onDidUpdateBdcStatus(bdcStatus => this.handleBdcStatusUpdate(bdcStatus));
|
||||
this.model.onBdcError(errorEvent => this.handleError(errorEvent));
|
||||
}
|
||||
|
||||
public showDashboard(): void {
|
||||
@@ -73,6 +76,7 @@ export class BdcDashboard {
|
||||
}).component();
|
||||
|
||||
this.refreshButton.onDidClick(async () => {
|
||||
this.overviewPage.onRefreshStarted();
|
||||
await this.doRefresh();
|
||||
});
|
||||
|
||||
@@ -83,7 +87,7 @@ export class BdcDashboard {
|
||||
}).component();
|
||||
|
||||
openTroubleshootNotebookButton.onDidClick(() => {
|
||||
vscode.commands.executeCommand('books.sqlserver2019');
|
||||
vscode.commands.executeCommand('books.sqlserver2019', getTroubleshootNotebookUrl(this.currentTab.serviceName));
|
||||
});
|
||||
|
||||
const toolbarContainer = modelView.modelBuilder.toolbarContainer()
|
||||
@@ -128,19 +132,20 @@ export class BdcDashboard {
|
||||
const overviewNavItemText = modelView.modelBuilder.text().withProperties({ value: localize('bdc.dashboard.overviewNavTitle', 'Big data cluster overview') }).component();
|
||||
overviewNavItemText.updateCssStyles(selectedTabCss);
|
||||
overviewNavItemDiv.addItem(overviewNavItemText, { CSSStyles: { 'user-select': 'text' } });
|
||||
const overviewPage = new BdcDashboardOverviewPage(this, this.model).create(modelView);
|
||||
this.currentPage = overviewPage;
|
||||
this.currentTab = { div: overviewNavItemDiv, dot: undefined, text: overviewNavItemText };
|
||||
this.mainAreaContainer.addItem(overviewPage, { flex: '0 0 100%', CSSStyles: { 'margin': '0 20px 0 20px' } });
|
||||
this.overviewPage = new BdcDashboardOverviewPage(this, this.model);
|
||||
const overviewContainer: azdata.FlexContainer = this.overviewPage.create(modelView);
|
||||
this.currentPage = overviewContainer;
|
||||
this.currentTab = { serviceName: undefined, div: overviewNavItemDiv, dot: undefined, text: overviewNavItemText };
|
||||
this.mainAreaContainer.addItem(overviewContainer, { flex: '0 0 100%', CSSStyles: { 'margin': '0 20px 0 20px' } });
|
||||
|
||||
overviewNavItemDiv.onDidClick(() => {
|
||||
if (this.currentTab) {
|
||||
this.currentTab.text.updateCssStyles(unselectedTabCss);
|
||||
}
|
||||
this.mainAreaContainer.removeItem(this.currentPage);
|
||||
this.mainAreaContainer.addItem(overviewPage, { flex: '0 0 100%', CSSStyles: { 'margin': '0 20px 0 20px' } });
|
||||
this.currentPage = overviewPage;
|
||||
this.currentTab = { div: overviewNavItemDiv, dot: undefined, text: overviewNavItemText };
|
||||
this.mainAreaContainer.addItem(overviewContainer, { flex: '0 0 100%', CSSStyles: { 'margin': '0 20px 0 20px' } });
|
||||
this.currentPage = overviewContainer;
|
||||
this.currentTab = { serviceName: undefined, div: overviewNavItemDiv, dot: undefined, text: overviewNavItemText };
|
||||
this.currentTab.text.updateCssStyles(selectedTabCss);
|
||||
});
|
||||
this.navContainer.addItem(overviewNavItemDiv, { flex: '0 0 auto' });
|
||||
@@ -165,6 +170,17 @@ export class BdcDashboard {
|
||||
this.updateServiceNavTabs(bdcStatus.services);
|
||||
}
|
||||
|
||||
private handleError(errorEvent: BdcErrorEvent): void {
|
||||
if (errorEvent.errorType !== 'general') {
|
||||
return;
|
||||
}
|
||||
// We don't want to show an error for the connection dialog being
|
||||
// canceled since that's a normal case.
|
||||
if (!(errorEvent.error instanceof HdfsDialogCancelledError)) {
|
||||
showErrorMessage(errorEvent.error.message);
|
||||
}
|
||||
}
|
||||
|
||||
private async doRefresh(): Promise<void> {
|
||||
try {
|
||||
this.refreshButton.enabled = false;
|
||||
@@ -227,5 +243,5 @@ function createServiceNavTab(modelBuilder: azdata.ModelBuilder, serviceStatus: S
|
||||
const text = modelBuilder.text().withProperties({ value: getServiceNameDisplayText(serviceStatus.serviceName), CSSStyles: { ...cssStyles.tabHeaderText } }).component();
|
||||
innerContainer.addItem(text, { flex: '0 0 auto' });
|
||||
div.addItem(innerContainer);
|
||||
return { div: div, dot: dot, text: text };
|
||||
return { serviceName: serviceStatus.serviceName, div: div, dot: dot, text: text };
|
||||
}
|
||||
|
||||
@@ -7,11 +7,16 @@ import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { ClusterController } from '../controller/clusterControllerApi';
|
||||
import { EndpointModel, BdcStatusModel } from '../controller/apiGenerated';
|
||||
import { showErrorMessage, Endpoint } from '../utils';
|
||||
import { Endpoint, Service } from '../utils';
|
||||
import { AuthType } from '../constants';
|
||||
import { ConnectControllerDialog, ConnectControllerModel } from './connectControllerDialog';
|
||||
import { ControllerTreeDataProvider } from '../tree/controllerTreeDataProvider';
|
||||
|
||||
export type BdcDashboardOptions = { url: string, auth: AuthType, username: string, password: string };
|
||||
|
||||
export type BdcErrorType = 'bdcStatus' | 'bdcEndpoints' | 'general';
|
||||
export type BdcErrorEvent = { error: Error, errorType: BdcErrorType };
|
||||
|
||||
export class BdcDashboardModel {
|
||||
|
||||
private _clusterController: ClusterController;
|
||||
@@ -21,12 +26,23 @@ export class BdcDashboardModel {
|
||||
private _endpointsLastUpdated: Date;
|
||||
private readonly _onDidUpdateEndpoints = new vscode.EventEmitter<EndpointModel[]>();
|
||||
private readonly _onDidUpdateBdcStatus = new vscode.EventEmitter<BdcStatusModel>();
|
||||
private readonly _onBdcError = new vscode.EventEmitter<BdcErrorEvent>();
|
||||
public onDidUpdateEndpoints = this._onDidUpdateEndpoints.event;
|
||||
public onDidUpdateBdcStatus = this._onDidUpdateBdcStatus.event;
|
||||
public onBdcError = this._onBdcError.event;
|
||||
|
||||
constructor(private options: BdcDashboardOptions, ignoreSslVerification = true) {
|
||||
this._clusterController = new ClusterController(options.url, options.auth, options.username, options.password, ignoreSslVerification);
|
||||
this.refresh();
|
||||
constructor(private _options: BdcDashboardOptions, private _treeDataProvider: ControllerTreeDataProvider) {
|
||||
try {
|
||||
this._clusterController = new ClusterController(_options.url, _options.auth, _options.username, _options.password);
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
this.refresh();
|
||||
} catch {
|
||||
this.promptReconnect().then(async () => {
|
||||
await this.refresh();
|
||||
}).catch(error => {
|
||||
this._onBdcError.fire({ error: error, errorType: 'general' });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public get bdcStatus(): BdcStatusModel | undefined {
|
||||
@@ -46,21 +62,28 @@ export class BdcDashboardModel {
|
||||
}
|
||||
|
||||
public async refresh(): Promise<void> {
|
||||
await Promise.all([
|
||||
this._clusterController.getBdcStatus(true).then(response => {
|
||||
this._bdcStatus = response.bdcStatus;
|
||||
this._bdcStatusLastUpdated = new Date();
|
||||
this._onDidUpdateBdcStatus.fire(this.bdcStatus);
|
||||
}),
|
||||
this._clusterController.getEndPoints(true).then(response => {
|
||||
this._endpoints = response.endPoints || [];
|
||||
fixEndpoints(this._endpoints);
|
||||
this._endpointsLastUpdated = new Date();
|
||||
this._onDidUpdateEndpoints.fire(this.serviceEndpoints);
|
||||
})
|
||||
]).catch(error => {
|
||||
showErrorMessage(error);
|
||||
});
|
||||
try {
|
||||
if (!this._clusterController) {
|
||||
// If this succeeds without error we know we have a clusterController at this point
|
||||
await this.promptReconnect();
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
this._clusterController.getBdcStatus(true).then(response => {
|
||||
this._bdcStatus = response.bdcStatus;
|
||||
this._bdcStatusLastUpdated = new Date();
|
||||
this._onDidUpdateBdcStatus.fire(this.bdcStatus);
|
||||
}).catch(error => this._onBdcError.fire({ error: error, errorType: 'bdcStatus' })),
|
||||
this._clusterController.getEndPoints(true).then(response => {
|
||||
this._endpoints = response.endPoints || [];
|
||||
fixEndpoints(this._endpoints);
|
||||
this._endpointsLastUpdated = new Date();
|
||||
this._onDidUpdateEndpoints.fire(this.serviceEndpoints);
|
||||
}).catch(error => this._onBdcError.fire({ error: error, errorType: 'bdcEndpoints' }))
|
||||
]);
|
||||
} catch (error) {
|
||||
this._onBdcError.fire({ error: error, errorType: 'general' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +104,7 @@ export class BdcDashboardModel {
|
||||
serverName: sqlServerMasterEndpoint.endpoint,
|
||||
databaseName: undefined,
|
||||
userName: 'sa',
|
||||
password: this.options.password,
|
||||
password: this._options.password,
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: undefined,
|
||||
@@ -92,6 +115,43 @@ export class BdcDashboardModel {
|
||||
options: {}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens up a dialog prompting the user to re-enter credentials for the controller
|
||||
*/
|
||||
private async promptReconnect(): Promise<void> {
|
||||
this._clusterController = await new ConnectControllerDialog(new ConnectControllerModel(this._options)).showDialog();
|
||||
this._treeDataProvider.addOrUpdateController(
|
||||
this._clusterController.url,
|
||||
this._clusterController.authType,
|
||||
this._clusterController.username,
|
||||
this._clusterController.password,
|
||||
/* Remember password */false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the troubleshoot book URL for the specified service, defaulting to the BDC
|
||||
* troubleshoot notebook if the service name is unknown.
|
||||
* @param service The service name to get the troubleshoot notebook URL for
|
||||
*/
|
||||
export function getTroubleshootNotebookUrl(service?: string): string {
|
||||
service = service || '';
|
||||
switch (service.toLowerCase()) {
|
||||
case Service.sql:
|
||||
return 'troubleshooters/tsg101-troubleshoot-sql-server';
|
||||
case Service.hdfs:
|
||||
return 'troubleshooters/tsg102-troubleshoot-hdfs';
|
||||
case Service.spark:
|
||||
return 'troubleshooters/tsg103-troubleshoot-spark';
|
||||
case Service.control:
|
||||
return 'troubleshooters/tsg104-troubleshoot-control';
|
||||
case Service.gateway:
|
||||
return 'troubleshooters/tsg105-troubleshoot-gateway';
|
||||
case Service.app:
|
||||
return 'troubleshooters/tsg106-troubleshoot-app';
|
||||
}
|
||||
return 'troubleshooters/tsg100-troubleshoot-bdc';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,25 +3,26 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { BdcDashboardModel } from './bdcDashboardModel';
|
||||
import { BdcDashboardModel, BdcErrorEvent } from './bdcDashboardModel';
|
||||
import { IconPathHelper, cssStyles } from '../constants';
|
||||
import { getStateDisplayText, getHealthStatusDisplayText, getEndpointDisplayText, getHealthStatusIcon, getServiceNameDisplayText, Endpoint } from '../utils';
|
||||
import { getStateDisplayText, getHealthStatusDisplayText, getEndpointDisplayText, getHealthStatusIcon, getServiceNameDisplayText, Endpoint, getBdcStatusErrorMessage } from '../utils';
|
||||
import { EndpointModel, ServiceStatusModel, BdcStatusModel } from '../controller/apiGenerated';
|
||||
import { BdcDashboard } from './bdcDashboard';
|
||||
import { createViewDetailsButton } from './commonControls';
|
||||
import { HdfsDialogCancelledError } from './hdfsDialogBase';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
const clusterStateColumnWidth = 125;
|
||||
const clusterStateLabelColumnWidth = 100;
|
||||
const clusterStateValueColumnWidth = 225;
|
||||
const healthStatusColumnWidth = 125;
|
||||
|
||||
const overviewIconColumnWidthPx = 25;
|
||||
const overviewServiceNameCellWidthPx = 175;
|
||||
const overviewStateCellWidthPx = 75;
|
||||
const overviewStateCellWidthPx = 150;
|
||||
const overviewHealthStatusCellWidthPx = 100;
|
||||
|
||||
const serviceEndpointRowServiceNameCellWidth = 200;
|
||||
@@ -35,16 +36,23 @@ export class BdcDashboardOverviewPage {
|
||||
private modelBuilder: azdata.ModelBuilder;
|
||||
|
||||
private lastUpdatedLabel: azdata.TextComponent;
|
||||
private propertiesContainer: azdata.DivContainer;
|
||||
private clusterStateLoadingComponent: azdata.LoadingComponent;
|
||||
private clusterHealthStatusLoadingComponent: azdata.LoadingComponent;
|
||||
|
||||
private serviceStatusRowContainer: azdata.FlexContainer;
|
||||
|
||||
private endpointsRowContainer: azdata.FlexContainer;
|
||||
private endpointsDisplayContainer: azdata.DivContainer;
|
||||
private serviceStatusDisplayContainer: azdata.DivContainer;
|
||||
private propertiesErrorMessage: azdata.TextComponent;
|
||||
private endpointsErrorMessage: azdata.TextComponent;
|
||||
private serviceStatusErrorMessage: azdata.TextComponent;
|
||||
|
||||
constructor(private dashboard: BdcDashboard, private model: BdcDashboardModel) {
|
||||
this.model.onDidUpdateEndpoints(endpoints => this.handleEndpointsUpdate(endpoints));
|
||||
this.model.onDidUpdateBdcStatus(bdcStatus => this.handleBdcStatusUpdate(bdcStatus));
|
||||
this.model.onBdcError(error => this.handleBdcError(error));
|
||||
}
|
||||
|
||||
public create(view: azdata.ModelView): azdata.FlexContainer {
|
||||
@@ -65,6 +73,11 @@ export class BdcDashboardOverviewPage {
|
||||
.component();
|
||||
rootContainer.addItem(propertiesLabel, { CSSStyles: { 'margin-top': '15px', 'padding-left': '10px', ...cssStyles.title } });
|
||||
|
||||
this.propertiesErrorMessage = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ display: 'none', CSSStyles: { ...cssStyles.errorText } }).component();
|
||||
rootContainer.addItem(this.propertiesErrorMessage, { flex: '0 0 auto' });
|
||||
|
||||
this.propertiesContainer = view.modelBuilder.divContainer().component();
|
||||
|
||||
// Row 1
|
||||
const row1 = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', height: '30px', alignItems: 'center' }).component();
|
||||
|
||||
@@ -72,8 +85,8 @@ export class BdcDashboardOverviewPage {
|
||||
const clusterStateLabel = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.clusterState', "Cluster State :") }).component();
|
||||
const clusterStateValue = view.modelBuilder.text().component();
|
||||
this.clusterStateLoadingComponent = view.modelBuilder.loadingComponent().withItem(clusterStateValue).component();
|
||||
row1.addItem(clusterStateLabel, { CSSStyles: { 'width': `${clusterStateColumnWidth}px`, 'min-width': `${clusterStateColumnWidth}px`, 'user-select': 'none', 'font-weight': 'bold' } });
|
||||
row1.addItem(this.clusterStateLoadingComponent, { CSSStyles: { 'width': `${clusterStateColumnWidth}px`, 'min-width': `${clusterStateColumnWidth}px` } });
|
||||
row1.addItem(clusterStateLabel, { CSSStyles: { 'width': `${clusterStateLabelColumnWidth}px`, 'min-width': `${clusterStateLabelColumnWidth}px`, 'user-select': 'none', 'font-weight': 'bold' } });
|
||||
row1.addItem(this.clusterStateLoadingComponent, { CSSStyles: { 'width': `${clusterStateValueColumnWidth}px`, 'min-width': `${clusterStateValueColumnWidth}px` } });
|
||||
|
||||
// Health Status
|
||||
const healthStatusLabel = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.healthStatus', "Health Status :") }).component();
|
||||
@@ -82,7 +95,9 @@ export class BdcDashboardOverviewPage {
|
||||
row1.addItem(healthStatusLabel, { CSSStyles: { 'width': `${healthStatusColumnWidth}px`, 'min-width': `${healthStatusColumnWidth}px`, 'user-select': 'none', 'font-weight': 'bold' } });
|
||||
row1.addItem(this.clusterHealthStatusLoadingComponent, { CSSStyles: { 'width': `${healthStatusColumnWidth}px`, 'min-width': `${healthStatusColumnWidth}px` } });
|
||||
|
||||
rootContainer.addItem(row1, { CSSStyles: { 'padding-left': '10px', 'border-bottom': 'solid 1px #ccc', 'box-sizing': 'border-box', 'user-select': 'text' } });
|
||||
this.propertiesContainer.addItem(row1, { CSSStyles: { 'padding-left': '10px', 'border-bottom': 'solid 1px #ccc', 'box-sizing': 'border-box', 'user-select': 'text' } });
|
||||
|
||||
rootContainer.addItem(this.propertiesContainer, { flex: '0 0 auto' });
|
||||
|
||||
// ############
|
||||
// # OVERVIEW #
|
||||
@@ -121,6 +136,8 @@ export class BdcDashboardOverviewPage {
|
||||
serviceStatusHeaderRow.addItem(healthStatusCell, { CSSStyles: { 'width': `${overviewHealthStatusCellWidthPx}px`, 'min-width': `${overviewHealthStatusCellWidthPx}px` } });
|
||||
overviewContainer.addItem(serviceStatusHeaderRow, { CSSStyles: { 'padding-left': '10px', 'box-sizing': 'border-box', 'user-select': 'text' } });
|
||||
|
||||
this.serviceStatusDisplayContainer = view.modelBuilder.divContainer().component();
|
||||
|
||||
// Service Status row container
|
||||
this.serviceStatusRowContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
|
||||
// Note we don't give the rows container as a child of the loading component since in order to align the loading component correctly
|
||||
@@ -131,7 +148,12 @@ export class BdcDashboardOverviewPage {
|
||||
.component();
|
||||
this.serviceStatusRowContainer.addItem(serviceStatusRowContainerLoadingComponent, { flex: '0 0 auto', CSSStyles: { 'padding-left': '150px', width: '30px' } });
|
||||
|
||||
overviewContainer.addItem(this.serviceStatusRowContainer);
|
||||
this.serviceStatusErrorMessage = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ display: 'none', CSSStyles: { ...cssStyles.errorText } }).component();
|
||||
overviewContainer.addItem(this.serviceStatusErrorMessage);
|
||||
|
||||
this.serviceStatusDisplayContainer.addItem(this.serviceStatusRowContainer);
|
||||
overviewContainer.addItem(this.serviceStatusDisplayContainer);
|
||||
|
||||
rootContainer.addItem(overviewContainer, { flex: '0 0 auto' });
|
||||
|
||||
// #####################
|
||||
@@ -143,6 +165,8 @@ export class BdcDashboardOverviewPage {
|
||||
.component();
|
||||
rootContainer.addItem(endpointsLabel, { CSSStyles: { 'padding-left': '10px', ...cssStyles.title } });
|
||||
|
||||
this.endpointsErrorMessage = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ display: 'none', CSSStyles: { ...cssStyles.errorText } }).component();
|
||||
|
||||
const endpointsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '100%', height: '100%' }).component();
|
||||
|
||||
// Service endpoints header row
|
||||
@@ -153,6 +177,7 @@ export class BdcDashboardOverviewPage {
|
||||
endpointsHeaderRow.addItem(endpointsEndpointHeaderCell, { CSSStyles: { 'width': `${serviceEndpointRowEndpointCellWidth}px`, 'min-width': `${serviceEndpointRowEndpointCellWidth}px`, ...cssStyles.tableHeader } });
|
||||
endpointsContainer.addItem(endpointsHeaderRow, { CSSStyles: { 'padding-left': '10px', 'box-sizing': 'border-box', 'user-select': 'text' } });
|
||||
|
||||
this.endpointsDisplayContainer = view.modelBuilder.divContainer().component();
|
||||
this.endpointsRowContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component();
|
||||
// Note we don't give the rows container as a child of the loading component since in order to align the loading component correctly
|
||||
// messes up the layout for the row container that we display after loading is finished. Instead we just remove the loading component
|
||||
@@ -162,8 +187,9 @@ export class BdcDashboardOverviewPage {
|
||||
.component();
|
||||
this.endpointsRowContainer.addItem(endpointRowContainerLoadingComponent, { flex: '0 0 auto', CSSStyles: { 'padding-left': '150px', width: '30px' } });
|
||||
|
||||
endpointsContainer.addItem(this.endpointsRowContainer);
|
||||
|
||||
this.endpointsDisplayContainer.addItem(this.endpointsRowContainer);
|
||||
endpointsContainer.addItem(this.endpointsErrorMessage);
|
||||
endpointsContainer.addItem(this.endpointsDisplayContainer);
|
||||
rootContainer.addItem(endpointsContainer, { flex: '0 0 auto' });
|
||||
|
||||
this.initialized = true;
|
||||
@@ -175,10 +201,22 @@ export class BdcDashboardOverviewPage {
|
||||
return rootContainer;
|
||||
}
|
||||
|
||||
public onRefreshStarted(): void {
|
||||
this.propertiesErrorMessage.display = 'none';
|
||||
this.serviceStatusErrorMessage.display = 'none';
|
||||
this.endpointsErrorMessage.display = 'none';
|
||||
|
||||
this.serviceStatusDisplayContainer.display = undefined;
|
||||
this.propertiesContainer.display = undefined;
|
||||
this.endpointsDisplayContainer.display = undefined;
|
||||
|
||||
|
||||
}
|
||||
private handleBdcStatusUpdate(bdcStatus: BdcStatusModel): void {
|
||||
if (!this.initialized || !bdcStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastUpdatedLabel.value =
|
||||
localize('bdc.dashboard.lastUpdated', "Last Updated : {0}",
|
||||
this.model.bdcStatusLastUpdated ?
|
||||
@@ -221,6 +259,44 @@ export class BdcDashboardOverviewPage {
|
||||
});
|
||||
}
|
||||
|
||||
private handleBdcError(errorEvent: BdcErrorEvent): void {
|
||||
if (errorEvent.errorType === 'bdcEndpoints') {
|
||||
const errorMessage = localize('endpointsError', "Unexpected error retrieving BDC Endpoints: {0}", errorEvent.error.message);
|
||||
this.showEndpointsError(errorMessage);
|
||||
} else if (errorEvent.errorType === 'bdcStatus') {
|
||||
this.showBdcStatusError(getBdcStatusErrorMessage(errorEvent.error));
|
||||
} else {
|
||||
this.handleGeneralError(errorEvent.error);
|
||||
}
|
||||
}
|
||||
|
||||
private showBdcStatusError(errorMessage: string): void {
|
||||
this.serviceStatusDisplayContainer.display = 'none';
|
||||
this.propertiesContainer.display = 'none';
|
||||
this.serviceStatusErrorMessage.value = errorMessage;
|
||||
this.serviceStatusErrorMessage.display = undefined;
|
||||
this.propertiesErrorMessage.value = errorMessage;
|
||||
this.propertiesErrorMessage.display = undefined;
|
||||
}
|
||||
|
||||
private showEndpointsError(errorMessage: string): void {
|
||||
this.endpointsDisplayContainer.display = 'none';
|
||||
this.endpointsErrorMessage.display = undefined;
|
||||
this.endpointsErrorMessage.value = errorMessage;
|
||||
}
|
||||
|
||||
private handleGeneralError(error: Error): void {
|
||||
if (error instanceof HdfsDialogCancelledError) {
|
||||
const errorMessage = localize('bdc.dashboard.noConnection', "The dashboard requires a connection. Please click retry to enter your credentials.");
|
||||
this.showBdcStatusError(errorMessage);
|
||||
this.showEndpointsError(errorMessage);
|
||||
} else {
|
||||
const errorMessage = localize('bdc.dashboard.unexpectedError', "Unexpected error occurred: {0}", error.message);
|
||||
this.showBdcStatusError(errorMessage);
|
||||
this.showEndpointsError(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private createServiceStatusRow(container: azdata.FlexContainer, serviceStatus: ServiceStatusModel, isLastRow: boolean): void {
|
||||
const serviceStatusRow = this.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', alignItems: 'center', height: '30px' }).component();
|
||||
const statusIconCell = this.modelBuilder.text().withProperties({ value: getHealthStatusIcon(serviceStatus.healthStatus), CSSStyles: { 'user-select': 'none' } }).component();
|
||||
@@ -230,17 +306,15 @@ export class BdcDashboardOverviewPage {
|
||||
this.dashboard.switchToServiceTab(serviceStatus.serviceName);
|
||||
});
|
||||
serviceStatusRow.addItem(nameCell, { CSSStyles: { 'width': `${overviewServiceNameCellWidthPx}px`, 'min-width': `${overviewServiceNameCellWidthPx}px`, ...cssStyles.text } });
|
||||
const stateCell = this.modelBuilder.text().withProperties({ value: getStateDisplayText(serviceStatus.state), CSSStyles: { ...cssStyles.text } }).component();
|
||||
const stateText = getStateDisplayText(serviceStatus.state);
|
||||
const stateCell = this.modelBuilder.text().withProperties({ value: stateText, title: stateText, CSSStyles: { ...cssStyles.overflowEllipsisText } }).component();
|
||||
serviceStatusRow.addItem(stateCell, { CSSStyles: { 'width': `${overviewStateCellWidthPx}px`, 'min-width': `${overviewStateCellWidthPx}px` } });
|
||||
const healthStatusCell = this.modelBuilder.text().withProperties({ value: getHealthStatusDisplayText(serviceStatus.healthStatus), CSSStyles: { ...cssStyles.text } }).component();
|
||||
const healthStatusText = getHealthStatusDisplayText(serviceStatus.healthStatus);
|
||||
const healthStatusCell = this.modelBuilder.text().withProperties({ value: healthStatusText, title: healthStatusText, CSSStyles: { ...cssStyles.overflowEllipsisText } }).component();
|
||||
serviceStatusRow.addItem(healthStatusCell, { CSSStyles: { 'width': `${overviewHealthStatusCellWidthPx}px`, 'min-width': `${overviewHealthStatusCellWidthPx}px` } });
|
||||
|
||||
if (serviceStatus.healthStatus !== 'healthy' && serviceStatus.details && serviceStatus.details.length > 0) {
|
||||
const viewDetailsButton = this.modelBuilder.button().withProperties<azdata.ButtonProperties>({ label: localize('bdc.dashboard.viewDetails', "View Details") }).component();
|
||||
viewDetailsButton.onDidClick(() => {
|
||||
vscode.window.showErrorMessage(serviceStatus.details);
|
||||
});
|
||||
serviceStatusRow.addItem(viewDetailsButton, { flex: '0 0 auto' });
|
||||
serviceStatusRow.addItem(createViewDetailsButton(this.modelBuilder, serviceStatus.details), { flex: '0 0 auto' });
|
||||
}
|
||||
|
||||
container.addItem(serviceStatusRow, { CSSStyles: { 'padding-left': '10px', 'border-top': 'solid 1px #ccc', 'border-bottom': isLastRow ? 'solid 1px #ccc' : '', 'box-sizing': 'border-box', 'user-select': 'text' } });
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
'use strict';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { BdcDashboardModel } from './bdcDashboardModel';
|
||||
import { BdcStatusModel, InstanceStatusModel } from '../controller/apiGenerated';
|
||||
import { getHealthStatusDisplayText, getHealthStatusIcon, getStateDisplayText } from '../utils';
|
||||
import { getHealthStatusDisplayText, getHealthStatusIcon, getStateDisplayText, Service } from '../utils';
|
||||
import { cssStyles } from '../constants';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
import { createViewDetailsButton } from './commonControls';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -28,11 +28,12 @@ export interface IInstanceStatus {
|
||||
|
||||
const healthAndStatusIconColumnWidth = 25;
|
||||
const healthAndStatusInstanceNameColumnWidth = 100;
|
||||
const healthAndStatusStateColumnWidth = 75;
|
||||
const healthAndStatusStateColumnWidth = 150;
|
||||
const healthAndStatusHealthColumnWidth = 100;
|
||||
|
||||
const metricsAndLogsInstanceNameColumnWidth = 125;
|
||||
const metricsAndLogsMetricsColumnWidth = 75;
|
||||
const metricsAndLogsNodeMetricsColumnWidth = 80;
|
||||
const metricsAndLogsSqlMetricsColumnWidth = 80;
|
||||
const metricsAndLogsLogsColumnWidth = 75;
|
||||
|
||||
const viewText = localize('bdc.dashboard.viewHyperlink', "View");
|
||||
@@ -44,6 +45,7 @@ export class BdcDashboardResourceStatusPage {
|
||||
private instanceHealthStatusRowsContainer: azdata.FlexContainer;
|
||||
private metricsAndLogsRowsContainer: azdata.FlexContainer;
|
||||
private lastUpdatedLabel: azdata.TextComponent;
|
||||
private sqlMetricsComponents: azdata.Component[];
|
||||
private initialized: boolean = false;
|
||||
|
||||
constructor(private model: BdcDashboardModel, private modelView: azdata.ModelView, private serviceName: string, private resourceName: string) {
|
||||
@@ -117,8 +119,13 @@ export class BdcDashboardResourceStatusPage {
|
||||
const metricsAndLogsHeaderRow = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row' }).component();
|
||||
const nameCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.instanceHeader', "Instance") }).component();
|
||||
metricsAndLogsHeaderRow.addItem(nameCell, { CSSStyles: { 'width': `${metricsAndLogsInstanceNameColumnWidth}px`, 'min-width': `${metricsAndLogsInstanceNameColumnWidth}px`, ...cssStyles.tableHeader } });
|
||||
const metricsCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.metricsHeader', "Metrics") }).component();
|
||||
metricsAndLogsHeaderRow.addItem(metricsCell, { CSSStyles: { 'width': `${metricsAndLogsMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsMetricsColumnWidth}px`, ...cssStyles.tableHeader } });
|
||||
const nodeMetricsCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.nodeMetricsHeader', "Node Metrics") }).component();
|
||||
metricsAndLogsHeaderRow.addItem(nodeMetricsCell, { CSSStyles: { 'width': `${metricsAndLogsNodeMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsNodeMetricsColumnWidth}px`, ...cssStyles.tableHeader } });
|
||||
// Only show SQL metrics column for SQL resource instances
|
||||
if (this.serviceName.toLowerCase() === Service.sql) {
|
||||
const sqlMetricsCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.sqlMetricsHeader', "SQL Metrics") }).component();
|
||||
metricsAndLogsHeaderRow.addItem(sqlMetricsCell, { CSSStyles: { 'width': `${metricsAndLogsSqlMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsSqlMetricsColumnWidth}px`, ...cssStyles.tableHeader } });
|
||||
}
|
||||
const healthStatusCell = view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({ value: localize('bdc.dashboard.logsHeader', "Logs") }).component();
|
||||
metricsAndLogsHeaderRow.addItem(healthStatusCell, { CSSStyles: { 'width': `${metricsAndLogsLogsColumnWidth}px`, 'min-width': `${metricsAndLogsLogsColumnWidth}px`, ...cssStyles.tableHeader } });
|
||||
rootContainer.addItem(metricsAndLogsHeaderRow, { flex: '0 0 auto', CSSStyles: { 'padding-left': '10px', 'box-sizing': 'border-box' } });
|
||||
@@ -153,7 +160,7 @@ export class BdcDashboardResourceStatusPage {
|
||||
const instanceHealthStatusRow = createInstanceHealthStatusRow(this.modelView.modelBuilder, i);
|
||||
this.instanceHealthStatusRowsContainer.addItem(instanceHealthStatusRow, { CSSStyles: { 'padding-left': '10px', 'border-top': 'solid 1px #ccc', 'box-sizing': 'border-box', 'user-select': 'text' } });
|
||||
|
||||
let metricsAndLogsRow = createMetricsAndLogsRow(this.modelView.modelBuilder, i);
|
||||
let metricsAndLogsRow = createMetricsAndLogsRow(this.modelView.modelBuilder, i, this.serviceName);
|
||||
this.metricsAndLogsRowsContainer.addItem(metricsAndLogsRow, { CSSStyles: { 'padding-left': '10px', 'border-top': 'solid 1px #ccc', 'box-sizing': 'border-box', 'user-select': 'text' } });
|
||||
});
|
||||
}
|
||||
@@ -175,17 +182,15 @@ function createInstanceHealthStatusRow(modelBuilder: azdata.ModelBuilder, instan
|
||||
instanceHealthStatusRow.addItem(statusIconCell, { CSSStyles: { 'width': `${healthAndStatusIconColumnWidth}px`, 'min-width': `${healthAndStatusIconColumnWidth}px` } });
|
||||
const nameCell = modelBuilder.text().withProperties({ value: instanceStatus.instanceName, CSSStyles: { ...cssStyles.text } }).component();
|
||||
instanceHealthStatusRow.addItem(nameCell, { CSSStyles: { 'width': `${healthAndStatusInstanceNameColumnWidth}px`, 'min-width': `${healthAndStatusInstanceNameColumnWidth}px`, ...cssStyles.text } });
|
||||
const stateCell = modelBuilder.text().withProperties({ value: getStateDisplayText(instanceStatus.state), CSSStyles: { ...cssStyles.text } }).component();
|
||||
const stateText = getStateDisplayText(instanceStatus.state);
|
||||
const stateCell = modelBuilder.text().withProperties({ value: stateText, title: stateText, CSSStyles: { ...cssStyles.overflowEllipsisText } }).component();
|
||||
instanceHealthStatusRow.addItem(stateCell, { CSSStyles: { 'width': `${healthAndStatusStateColumnWidth}px`, 'min-width': `${healthAndStatusStateColumnWidth}px` } });
|
||||
const healthStatusCell = modelBuilder.text().withProperties({ value: getHealthStatusDisplayText(instanceStatus.healthStatus), CSSStyles: { ...cssStyles.text } }).component();
|
||||
const healthStatusText = getHealthStatusDisplayText(instanceStatus.healthStatus);
|
||||
const healthStatusCell = modelBuilder.text().withProperties({ value: healthStatusText, title: healthStatusText, CSSStyles: { ...cssStyles.overflowEllipsisText } }).component();
|
||||
instanceHealthStatusRow.addItem(healthStatusCell, { CSSStyles: { 'width': `${healthAndStatusHealthColumnWidth}px`, 'min-width': `${healthAndStatusHealthColumnWidth}px` } });
|
||||
|
||||
if (instanceStatus.healthStatus !== 'healthy' && instanceStatus.details && instanceStatus.details.length > 0) {
|
||||
const viewDetailsButton = modelBuilder.button().withProperties<azdata.ButtonProperties>({ label: localize('bdc.dashboard.viewDetails', "View Details") }).component();
|
||||
viewDetailsButton.onDidClick(() => {
|
||||
vscode.window.showErrorMessage(instanceStatus.details);
|
||||
});
|
||||
instanceHealthStatusRow.addItem(viewDetailsButton, { flex: '0 0 auto' });
|
||||
instanceHealthStatusRow.addItem(createViewDetailsButton(modelBuilder, instanceStatus.details), { flex: '0 0 auto' });
|
||||
}
|
||||
return instanceHealthStatusRow;
|
||||
}
|
||||
@@ -194,8 +199,9 @@ function createInstanceHealthStatusRow(modelBuilder: azdata.ModelBuilder, instan
|
||||
* Creates a row with the name, link to the metrics and a link to the logs for a particular instance on this resource
|
||||
* @param modelBuilder The builder used to create the component
|
||||
* @param instanceStatus The status object for the instance this row is for
|
||||
* @param serviceName The name of the service this resource instance belongs to
|
||||
*/
|
||||
function createMetricsAndLogsRow(modelBuilder: azdata.ModelBuilder, instanceStatus: InstanceStatusModel): azdata.FlexContainer {
|
||||
function createMetricsAndLogsRow(modelBuilder: azdata.ModelBuilder, instanceStatus: InstanceStatusModel, serviceName: string): azdata.FlexContainer {
|
||||
const metricsAndLogsRow = modelBuilder.flexContainer().withLayout({ flexFlow: 'row', alignItems: 'center', height: '30px' }).component();
|
||||
const nameCell = modelBuilder.text().withProperties({ value: instanceStatus.instanceName, CSSStyles: { ...cssStyles.text } }).component();
|
||||
metricsAndLogsRow.addItem(nameCell, { CSSStyles: { 'width': `${metricsAndLogsInstanceNameColumnWidth}px`, 'min-width': `${metricsAndLogsInstanceNameColumnWidth}px`, ...cssStyles.text } });
|
||||
@@ -203,17 +209,47 @@ function createMetricsAndLogsRow(modelBuilder: azdata.ModelBuilder, instanceStat
|
||||
// Not all instances have all logs available - in that case just display N/A instead of a link
|
||||
if (isNullOrUndefined(instanceStatus.dashboards) || isNullOrUndefined(instanceStatus.dashboards.nodeMetricsUrl)) {
|
||||
const metricsCell = modelBuilder.text().withProperties({ value: notAvailableText, CSSStyles: { ...cssStyles.text } }).component();
|
||||
metricsAndLogsRow.addItem(metricsCell, { CSSStyles: { 'width': `${metricsAndLogsMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsMetricsColumnWidth}px`, ...cssStyles.text } });
|
||||
metricsAndLogsRow.addItem(metricsCell, { CSSStyles: { 'width': `${metricsAndLogsNodeMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsNodeMetricsColumnWidth}px`, ...cssStyles.text } });
|
||||
} else {
|
||||
const metricsCell = modelBuilder.hyperlink().withProperties({ label: viewText, url: instanceStatus.dashboards.nodeMetricsUrl, CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink } }).component();
|
||||
metricsAndLogsRow.addItem(metricsCell, { CSSStyles: { 'width': `${metricsAndLogsMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsMetricsColumnWidth}px` } });
|
||||
const nodeMetricsCell = modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
|
||||
label: viewText,
|
||||
url: instanceStatus.dashboards.nodeMetricsUrl,
|
||||
title: instanceStatus.dashboards.nodeMetricsUrl,
|
||||
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
|
||||
})
|
||||
.component();
|
||||
metricsAndLogsRow.addItem(nodeMetricsCell, { CSSStyles: { 'width': `${metricsAndLogsNodeMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsNodeMetricsColumnWidth}px` } });
|
||||
}
|
||||
|
||||
// Only show SQL metrics column for SQL resource instances
|
||||
if (serviceName === Service.sql) {
|
||||
// Not all instances have all logs available - in that case just display N/A instead of a link
|
||||
if (isNullOrUndefined(instanceStatus.dashboards) || isNullOrUndefined(instanceStatus.dashboards.sqlMetricsUrl)) {
|
||||
const metricsCell = modelBuilder.text().withProperties({ value: notAvailableText, CSSStyles: { ...cssStyles.text } }).component();
|
||||
metricsAndLogsRow.addItem(metricsCell, { CSSStyles: { 'width': `${metricsAndLogsSqlMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsSqlMetricsColumnWidth}px`, ...cssStyles.text } });
|
||||
} else {
|
||||
const sqlMetricsCell = modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
|
||||
label: viewText,
|
||||
url: instanceStatus.dashboards.sqlMetricsUrl,
|
||||
title: instanceStatus.dashboards.sqlMetricsUrl,
|
||||
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
|
||||
})
|
||||
.component();
|
||||
metricsAndLogsRow.addItem(sqlMetricsCell, { CSSStyles: { 'width': `${metricsAndLogsSqlMetricsColumnWidth}px`, 'min-width': `${metricsAndLogsSqlMetricsColumnWidth}px` } });
|
||||
}
|
||||
}
|
||||
|
||||
if (isNullOrUndefined(instanceStatus.dashboards) || isNullOrUndefined(instanceStatus.dashboards.logsUrl)) {
|
||||
const logsCell = modelBuilder.text().withProperties({ value: notAvailableText, CSSStyles: { ...cssStyles.text } }).component();
|
||||
metricsAndLogsRow.addItem(logsCell, { CSSStyles: { 'width': `${metricsAndLogsLogsColumnWidth}px`, 'min-width': `${metricsAndLogsLogsColumnWidth}px`, ...cssStyles.text } });
|
||||
} else {
|
||||
const logsCell = modelBuilder.hyperlink().withProperties({ label: viewText, url: instanceStatus.dashboards.logsUrl, CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink } }).component();
|
||||
const logsCell = modelBuilder.hyperlink().withProperties<azdata.HyperlinkComponentProperties>({
|
||||
label: viewText,
|
||||
url: instanceStatus.dashboards.logsUrl,
|
||||
title: instanceStatus.dashboards.logsUrl,
|
||||
CSSStyles: { ...cssStyles.text, ...cssStyles.hyperlink }
|
||||
})
|
||||
.component();
|
||||
metricsAndLogsRow.addItem(logsCell, { CSSStyles: { 'width': `${metricsAndLogsLogsColumnWidth}px`, 'min-width': `${metricsAndLogsLogsColumnWidth}px` } });
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export function createViewDetailsButton(modelBuilder: azdata.ModelBuilder, text: string): azdata.ButtonComponent {
|
||||
const viewDetailsButton = modelBuilder.button().withProperties<azdata.ButtonProperties>({ label: localize('bdc.dashboard.viewDetails', "View Details") }).component();
|
||||
viewDetailsButton.onDidClick(() => {
|
||||
vscode.window.showErrorMessage(text, { modal: true });
|
||||
});
|
||||
return viewDetailsButton;
|
||||
}
|
||||
@@ -28,6 +28,12 @@ export interface HdfsDialogProperties {
|
||||
password?: string;
|
||||
}
|
||||
|
||||
export class HdfsDialogCancelledError extends Error {
|
||||
constructor(message: string = 'Dialog cancelled') {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class HdfsDialogModelBase<T extends HdfsDialogProperties, R> {
|
||||
protected _canceled = false;
|
||||
private _authTypes: azdata.CategoryValue[];
|
||||
@@ -74,7 +80,7 @@ export abstract class HdfsDialogModelBase<T extends HdfsDialogProperties, R> {
|
||||
}
|
||||
|
||||
protected createController(): ClusterController {
|
||||
return new ClusterController(this.props.url, this.props.auth, this.props.username, this.props.password, true);
|
||||
return new ClusterController(this.props.url, this.props.auth, this.props.username, this.props.password);
|
||||
}
|
||||
|
||||
protected async createAndVerifyControllerConnection(): Promise<ClusterController> {
|
||||
@@ -87,7 +93,7 @@ export abstract class HdfsDialogModelBase<T extends HdfsDialogProperties, R> {
|
||||
throw new Error(localize('mount.hdfs.loginerror1', "Login to controller failed"));
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(localize('mount.hdfs.loginerror2', "Login to controller failed: {0}", err.message));
|
||||
throw new Error(localize('mount.hdfs.loginerror2', "Login to controller failed: {0}", err.statusMessage || err.message));
|
||||
}
|
||||
return controller;
|
||||
}
|
||||
@@ -224,7 +230,7 @@ export abstract class HdfsDialogBase<T extends HdfsDialogProperties, R> {
|
||||
if (this.model && this.model.onCancel) {
|
||||
await this.model.onCancel();
|
||||
}
|
||||
this.returnPromise.reject(new Error('Dialog cancelled'));
|
||||
this.returnPromise.reject(new HdfsDialogCancelledError());
|
||||
}
|
||||
|
||||
protected async reportError(error: any): Promise<void> {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { HdfsDialogBase, HdfsDialogModelBase, HdfsDialogProperties } from './hdf
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
const mountConfigutationTitle = localize('mount.main.section', "Mount Configuration");
|
||||
const hdfsPathTitle = localize('mount.hdfsPath', "HDFS Path");
|
||||
const hdfsPathTitle = localize('mount.hdfsPath.title', "HDFS Path");
|
||||
|
||||
/**
|
||||
* Converts a comma-delimited set of key value pair credentials to a JSON object.
|
||||
@@ -178,15 +178,24 @@ export class MountHdfsDialog extends HdfsDialogBase<MountHdfsProperties, void> {
|
||||
{
|
||||
component: this.pathInputBox,
|
||||
title: hdfsPathTitle,
|
||||
required: true
|
||||
required: true,
|
||||
layout: {
|
||||
info: localize('mount.hdfsPath.info', "Path to a new (non-existing) directory which you want to associate with the mount")
|
||||
}
|
||||
}, {
|
||||
component: this.remoteUriInputBox,
|
||||
title: localize('mount.remoteUri', "Remote URI"),
|
||||
required: true
|
||||
title: localize('mount.remoteUri.title', "Remote URI"),
|
||||
required: true,
|
||||
layout: {
|
||||
info: localize('mount.remoteUri.info', "The URI to the remote data source. Example for ADLS: abfs://fs@saccount.dfs.core.windows.net/")
|
||||
}
|
||||
}, {
|
||||
component: this.credentialsInputBox,
|
||||
title: localize('mount.credentials', "Credentials"),
|
||||
required: false
|
||||
title: localize('mount.credentials.title', "Credentials"),
|
||||
required: false,
|
||||
layout: {
|
||||
info: localize('mount.credentials.info', "Mount credentials for authentication to remote data source for reads")
|
||||
}
|
||||
}
|
||||
],
|
||||
title: mountConfigutationTitle
|
||||
|
||||
@@ -45,7 +45,7 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
||||
return this.root.getChildren();
|
||||
}
|
||||
|
||||
this.loadSavedControllers();
|
||||
await this.loadSavedControllers();
|
||||
return [new LoadingControllerNode()];
|
||||
}
|
||||
|
||||
@@ -57,7 +57,15 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
||||
this._onDidChangeTreeData.fire(node);
|
||||
}
|
||||
|
||||
public addController(
|
||||
/**
|
||||
* Creates or updates a node in the tree with the specified connection information
|
||||
* @param url The URL for the BDC management endpoint
|
||||
* @param auth The type of auth to use
|
||||
* @param username The username (if basic auth)
|
||||
* @param password The password (if basic auth)
|
||||
* @param rememberPassword Whether to store the password in the password store when saving
|
||||
*/
|
||||
public addOrUpdateController(
|
||||
url: string,
|
||||
auth: AuthType,
|
||||
username: string,
|
||||
@@ -65,7 +73,7 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
||||
rememberPassword: boolean
|
||||
): void {
|
||||
this.removeNonControllerNodes();
|
||||
this.root.addControllerNode(url, auth, username, password, rememberPassword);
|
||||
this.root.addOrUpdateControllerNode(url, auth, username, password, rememberPassword);
|
||||
this.notifyNodeChanged();
|
||||
}
|
||||
|
||||
@@ -113,7 +121,7 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
||||
this.root.clearChildren();
|
||||
let controllers: IControllerInfoSlim[] = this.memento.get('controllers');
|
||||
if (controllers) {
|
||||
for (let c of controllers) {
|
||||
for (const c of controllers) {
|
||||
let password = undefined;
|
||||
if (c.rememberPassword) {
|
||||
password = await this.getPassword(c.url, c.username);
|
||||
@@ -138,18 +146,18 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
||||
}
|
||||
|
||||
public async saveControllers(): Promise<void> {
|
||||
let controllers = this.root.children.map((e): IControllerInfoSlim => {
|
||||
let controller = e as ControllerNode;
|
||||
const controllers = this.root.children.map((e): IControllerInfoSlim => {
|
||||
const controller = e as ControllerNode;
|
||||
return {
|
||||
url: controller.url,
|
||||
auth: controller.auth,
|
||||
username: controller.username,
|
||||
password: controller.password,
|
||||
rememberPassword: !!controller.rememberPassword
|
||||
rememberPassword: controller.rememberPassword
|
||||
};
|
||||
});
|
||||
|
||||
let controllersWithoutPassword = controllers.map((e): IControllerInfoSlim => {
|
||||
const controllersWithoutPassword = controllers.map((e): IControllerInfoSlim => {
|
||||
return {
|
||||
url: e.url,
|
||||
auth: e.auth,
|
||||
@@ -164,7 +172,7 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
||||
showErrorMessage(error);
|
||||
}
|
||||
|
||||
for (let e of controllers) {
|
||||
for (const e of controllers) {
|
||||
if (e.rememberPassword) {
|
||||
await this.savePassword(e.url, e.username, e.password);
|
||||
} else {
|
||||
@@ -187,7 +195,7 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
||||
return result;
|
||||
}
|
||||
|
||||
private async getPassword(url: string, username: string): Promise<string> {
|
||||
private async getPassword(url: string, username: string): Promise<string | undefined> {
|
||||
let provider = await this.getCredentialProvider();
|
||||
let id = this.createId(url, username);
|
||||
let credential = await provider.readCredential(id);
|
||||
|
||||
@@ -103,7 +103,15 @@ export class ControllerRootNode extends ControllerTreeNode {
|
||||
return this.children as ControllerNode[];
|
||||
}
|
||||
|
||||
public addControllerNode(
|
||||
/**
|
||||
* Creates or updates a node in the tree with the specified connection information
|
||||
* @param url The URL for the BDC management endpoint
|
||||
* @param auth The type of auth to use
|
||||
* @param username The username (if basic auth)
|
||||
* @param password The password (if basic auth)
|
||||
* @param rememberPassword Whether to store the password in the password store when saving
|
||||
*/
|
||||
public addOrUpdateControllerNode(
|
||||
url: string,
|
||||
auth: AuthType,
|
||||
username: string,
|
||||
@@ -201,6 +209,10 @@ export class ControllerNode extends ControllerTreeNode {
|
||||
this._password = pw;
|
||||
}
|
||||
|
||||
public set label(label: string) {
|
||||
super.label = label || this.generateLabel();
|
||||
}
|
||||
|
||||
public get rememberPassword() {
|
||||
return this._rememberPassword;
|
||||
}
|
||||
@@ -209,10 +221,6 @@ export class ControllerNode extends ControllerTreeNode {
|
||||
this._rememberPassword = rememberPassword;
|
||||
}
|
||||
|
||||
public set label(label: string) {
|
||||
super.label = label || this.generateLabel();
|
||||
}
|
||||
|
||||
private generateLabel(): string {
|
||||
let label = `controller: ${ControllerNode.toIpAndPort(this._url)}`;
|
||||
if (this._auth === 'basic') {
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as azdata from 'azdata';
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
import * as constants from './constants';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export enum Endpoint {
|
||||
gateway = 'gateway',
|
||||
@@ -71,36 +69,50 @@ export function showErrorMessage(error: any, prefixText?: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mappings of the different expected state values to their localized friendly names.
|
||||
* These are defined in aris/projects/controller/src/Microsoft.SqlServer.Controller/StateMachines
|
||||
*/
|
||||
const stateToDisplayTextMap = {
|
||||
// K8sScaledSetStateMachine
|
||||
'creating': localize('state.creating', "Creating"),
|
||||
'waiting': localize('state.waiting', "Waiting"),
|
||||
'ready': localize('state.ready', "Ready"),
|
||||
'deleting': localize('state.deleting', "Deleting"),
|
||||
'deleted': localize('state.deleted', "Deleted"),
|
||||
'applyingupgrade': localize('state.applyingUpgrade', "Applying Upgrade"),
|
||||
'upgrading': localize('state.upgrading', "Upgrading"),
|
||||
'applyingmanagedupgrade': localize('state.applyingmanagedupgrade', "Applying Managed Upgrade"),
|
||||
'managedupgrading': localize('state.managedUpgrading', "Managed Upgrading"),
|
||||
'rollback': localize('state.rollback', "Rollback"),
|
||||
'rollbackinprogress': localize('state.rollbackInProgress', "Rollback In Progress"),
|
||||
'rollbackcomplete': localize('state.rollbackComplete', "Rollback Complete"),
|
||||
'error': localize('state.error', "Error"),
|
||||
|
||||
// BigDataClusterStateMachine
|
||||
'creatingsecrets': localize('state.creatingSecrets', "Creating Secrets"),
|
||||
'waitingforsecrets': localize('state.waitingForSecrets', "Waiting For Secrets"),
|
||||
'creatinggroups': localize('state.creatingGroups', "Creating Groups"),
|
||||
'waitingforgroups': localize('state.waitingForGroups', "Waiting For Groups"),
|
||||
'creatingresources': localize('state.creatingResources', "Creating Resources"),
|
||||
'waitingforresources': localize('state.waitingForResources', "Waiting For Resources"),
|
||||
'creatingkerberosdelegationsetup': localize('state.creatingKerberosDelegationSetup', "Creating Kerberos Delegation Setup"),
|
||||
'waitingforkerberosdelegationsetup': localize('state.waitingForKerberosDelegationSetup', "Waiting For Kerberos Delegation Setup"),
|
||||
'waitingfordeletion': localize('state.waitingForDeletion', "Waiting For Deletion"),
|
||||
'waitingforupgrade': localize('state.waitingForUpgrade', "Waiting For Upgrade"),
|
||||
'upgradePaused': localize('state.upgradePaused', "Upgrade Paused"),
|
||||
|
||||
// Other
|
||||
'running': localize('state.running', "Running"),
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the localized text to display for a corresponding state
|
||||
* @param state The state to get the display text for
|
||||
*/
|
||||
export function getStateDisplayText(state?: string): string {
|
||||
state = state || '';
|
||||
switch (state.toLowerCase()) {
|
||||
case 'creating':
|
||||
return localize('state.creating', "Creating");
|
||||
case 'waiting':
|
||||
return localize('state.waiting', "Waiting");
|
||||
case 'ready':
|
||||
return localize('state.ready', "Ready");
|
||||
case 'deleting':
|
||||
return localize('state.deleting', "Deleting");
|
||||
case 'waitingfordeletion':
|
||||
return localize('state.waitingForDeletion', "Waiting For Deletion");
|
||||
case 'deleted':
|
||||
return localize('state.deleted', "Deleted");
|
||||
case 'upgrading':
|
||||
return localize('state.upgrading', "Upgrading");
|
||||
case 'waitingforupgrade':
|
||||
return localize('state.waitingForUpgrade', "Waiting For Upgrade");
|
||||
case 'error':
|
||||
return localize('state.error', "Error");
|
||||
case 'running':
|
||||
return localize('state.running', "Running");
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
return stateToDisplayTextMap[state.toLowerCase()] || state;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,3 +267,26 @@ export function getControllerEndpoint(serverInfo: azdata.ServerInfo): string | u
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getBdcStatusErrorMessage(error: Error): string {
|
||||
return localize('endpointsError', "Unexpected error retrieving BDC Endpoints: {0}", error.message);
|
||||
}
|
||||
|
||||
export const bdcConfigSectionName = 'bigDataCluster';
|
||||
export const ignoreSslConfigName = 'ignoreSslVerification';
|
||||
|
||||
/**
|
||||
* Retrieves the current setting for whether to ignore SSL verification errors
|
||||
*/
|
||||
export function getIgnoreSslVerificationConfigSetting(): boolean {
|
||||
try {
|
||||
const config = vscode.workspace.getConfiguration(bdcConfigSectionName);
|
||||
const value = config.get<boolean>(ignoreSslConfigName);
|
||||
return value !== undefined ? value : true;
|
||||
} catch (error) {
|
||||
console.error(`Unexpected error retrieving ${bdcConfigSectionName}.${ignoreSslConfigName} setting : ${error}`);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ import { getControllerEndpoint } from './bigDataCluster/utils';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
const AddControllerCommand = 'bigDataClusters.command.addController';
|
||||
const DeleteControllerCommand = 'bigDataClusters.command.deleteController';
|
||||
const RefreshControllerCommand = 'bigDataClusters.command.refreshController';
|
||||
const ManageControllerCommand = 'bigDataClusters.command.manageController';
|
||||
const MountHdfsCommand = 'bigDataClusters.command.mount';
|
||||
const RefreshMountCommand = 'bigDataClusters.command.refreshmount';
|
||||
const DeleteMountCommand = 'bigDataClusters.command.deletemount';
|
||||
export const AddControllerCommand = 'bigDataClusters.command.addController';
|
||||
export const DeleteControllerCommand = 'bigDataClusters.command.deleteController';
|
||||
export const RefreshControllerCommand = 'bigDataClusters.command.refreshController';
|
||||
export const ManageControllerCommand = 'bigDataClusters.command.manageController';
|
||||
export const MountHdfsCommand = 'bigDataClusters.command.mount';
|
||||
export const RefreshMountCommand = 'bigDataClusters.command.refreshmount';
|
||||
export const DeleteMountCommand = 'bigDataClusters.command.deletemount';
|
||||
|
||||
const endpointNotFoundError = localize('mount.error.endpointNotFound', "Controller endpoint information was not found");
|
||||
|
||||
@@ -51,8 +51,8 @@ function registerCommands(context: vscode.ExtensionContext, treeDataProvider: Co
|
||||
runThrottledAction(AddControllerCommand, () => addBdcController(treeDataProvider, node));
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand(DeleteControllerCommand, (node: TreeNode) => {
|
||||
deleteBdcController(treeDataProvider, node);
|
||||
vscode.commands.registerCommand(DeleteControllerCommand, async (node: TreeNode) => {
|
||||
await deleteBdcController(treeDataProvider, node);
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand(RefreshControllerCommand, (node: TreeNode) => {
|
||||
@@ -64,7 +64,7 @@ function registerCommands(context: vscode.ExtensionContext, treeDataProvider: Co
|
||||
|
||||
vscode.commands.registerCommand(ManageControllerCommand, async (info: ControllerNode | BdcDashboardOptions) => {
|
||||
const title: string = `${localize('bdc.dashboard.title', "Big Data Cluster Dashboard -")} ${ControllerNode.toIpAndPort(info.url)}`;
|
||||
const dashboard: BdcDashboard = new BdcDashboard(title, new BdcDashboardModel(info));
|
||||
const dashboard: BdcDashboard = new BdcDashboard(title, new BdcDashboardModel(info, treeDataProvider));
|
||||
dashboard.showDashboard();
|
||||
});
|
||||
|
||||
@@ -80,26 +80,26 @@ function registerCommands(context: vscode.ExtensionContext, treeDataProvider: Co
|
||||
}
|
||||
|
||||
async function mountHdfs(explorerContext?: azdata.ObjectExplorerContext): Promise<void> {
|
||||
let mountProps = await getMountProps(explorerContext);
|
||||
const mountProps = await getMountProps(explorerContext);
|
||||
if (mountProps) {
|
||||
let dialog = new MountHdfsDialog(new MountHdfsModel(mountProps));
|
||||
dialog.showDialog();
|
||||
const dialog = new MountHdfsDialog(new MountHdfsModel(mountProps));
|
||||
await dialog.showDialog();
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshMount(explorerContext?: azdata.ObjectExplorerContext): Promise<void> {
|
||||
let mountProps = await getMountProps(explorerContext);
|
||||
const mountProps = await getMountProps(explorerContext);
|
||||
if (mountProps) {
|
||||
let dialog = new RefreshMountDialog(new RefreshMountModel(mountProps));
|
||||
dialog.showDialog();
|
||||
const dialog = new RefreshMountDialog(new RefreshMountModel(mountProps));
|
||||
await dialog.showDialog();
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteMount(explorerContext?: azdata.ObjectExplorerContext): Promise<void> {
|
||||
let mountProps = await getMountProps(explorerContext);
|
||||
const mountProps = await getMountProps(explorerContext);
|
||||
if (mountProps) {
|
||||
let dialog = new DeleteMountDialog(new DeleteMountModel(mountProps));
|
||||
dialog.showDialog();
|
||||
const dialog = new DeleteMountDialog(new DeleteMountModel(mountProps));
|
||||
await dialog.showDialog();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,15 +169,15 @@ async function deleteBdcController(treeDataProvider: ControllerTreeDataProvider,
|
||||
let result = await vscode.window.showQuickPick(Object.keys(choices), options);
|
||||
let remove: boolean = !!(result && choices[result]);
|
||||
if (remove) {
|
||||
deleteControllerInternal(treeDataProvider, controllerNode);
|
||||
await deleteControllerInternal(treeDataProvider, controllerNode);
|
||||
}
|
||||
return remove;
|
||||
}
|
||||
|
||||
function deleteControllerInternal(treeDataProvider: ControllerTreeDataProvider, controllerNode: ControllerNode): void {
|
||||
let deleted = treeDataProvider.deleteController(controllerNode.url, controllerNode.auth, controllerNode.username);
|
||||
async function deleteControllerInternal(treeDataProvider: ControllerTreeDataProvider, controllerNode: ControllerNode): Promise<void> {
|
||||
const deleted = treeDataProvider.deleteController(controllerNode.url, controllerNode.auth, controllerNode.username);
|
||||
if (deleted) {
|
||||
treeDataProvider.saveControllers();
|
||||
await treeDataProvider.saveControllers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# Microsoft SQL Server Dacpac for Azure Data Studio
|
||||
|
||||
## Data-tier Application Wizard *(preview)*
|
||||
## Data-tier Application Wizard
|
||||
**The Data-tier Application Wizard** provides an easy to use experience to deploy and extract .dacpac files and import and export .bacpac files.
|
||||
|
||||
This experience is currently in its initial preview. Please report issues and feature requests [here.](https://github.com/microsoft/azuredatastudio/issues)
|
||||
Please report issues and feature requests [here.](https://github.com/microsoft/azuredatastudio/issues)
|
||||
|
||||
<img src="https://user-images.githubusercontent.com/30873802/49676289-f2df6880-fa2d-11e8-8bfa-6213b7734075.png" width="800px" />
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
"name": "dacpac",
|
||||
"displayName": "SQL Server Dacpac",
|
||||
"description": "SQL Server Dacpac for Azure Data Studio.",
|
||||
"version": "0.8.0",
|
||||
"version": "1.0.0",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"preview": false,
|
||||
"engines": {
|
||||
"vscode": "^1.25.0",
|
||||
"azdata": "*"
|
||||
@@ -35,17 +35,17 @@
|
||||
"objectExplorer/item/context": [
|
||||
{
|
||||
"command": "dacFx.start",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Database",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Database && mssql:engineedition != 11",
|
||||
"group": "export"
|
||||
},
|
||||
{
|
||||
"command": "dacFx.start",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Server",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Server && mssql:engineedition != 11",
|
||||
"group": "export"
|
||||
},
|
||||
{
|
||||
"command": "dacFx.start",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Folder && nodeLabel == 'Databases'",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Folder && nodeLabel == 'Databases' && mssql:engineedition != 11",
|
||||
"group": "export"
|
||||
}
|
||||
]
|
||||
@@ -53,7 +53,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"htmlparser2": "^3.10.1",
|
||||
"vscode-nls": "^3.2.1"
|
||||
"vscode-nls": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^5.2.5",
|
||||
|
||||
@@ -762,10 +762,10 @@ verror@1.10.0:
|
||||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.5"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.5.tgz#25520c1955108036dec607c85e00a522f247f1a4"
|
||||
integrity sha512-ITtoh3V4AkWXMmp3TB97vsMaHRgHhsSFPsUdzlueSL+dRZbSNTZeOmdQv60kjCV306ghPxhDeoNUEm3+EZMuyw==
|
||||
vscode-nls@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c"
|
||||
integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A==
|
||||
|
||||
vscode-test@^0.4.1:
|
||||
version "0.4.3"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "import",
|
||||
"displayName": "SQL Server Import",
|
||||
"description": "SQL Server Import for Azure Data Studio supports importing CSV or JSON files into SQL Server.",
|
||||
"version": "0.11.0",
|
||||
"version": "0.12.0",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"engines": {
|
||||
@@ -46,7 +46,7 @@
|
||||
"objectExplorer/item/context": [
|
||||
{
|
||||
"command": "flatFileImport.start",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Database",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Database && mssql:engineedition != 11",
|
||||
"group": "import"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
"id": "all-database-size-server-insight",
|
||||
"contrib": {
|
||||
"name": "Database Size (MB)",
|
||||
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud",
|
||||
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud && mssql:engineedition != 11",
|
||||
"gridItemConfig": {
|
||||
"x": 2,
|
||||
"y": 2
|
||||
@@ -83,7 +83,7 @@
|
||||
"contrib": {
|
||||
"cacheId": "backup-history-server-insight",
|
||||
"name": "Backup Status",
|
||||
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud",
|
||||
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud && mssql:engineedition != 11",
|
||||
"gridItemConfig": {
|
||||
"x": 1,
|
||||
"y": 1
|
||||
@@ -133,4 +133,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||
"version": "2.0.0-release.24",
|
||||
"version": "2.0.0-release.30",
|
||||
"downloadFileNames": {
|
||||
"Windows_86": "win-x86-netcoreapp2.2.zip",
|
||||
"Windows_64": "win-x64-netcoreapp2.2.zip",
|
||||
|
||||
@@ -308,11 +308,18 @@
|
||||
"flavors": [
|
||||
{
|
||||
"flavor": "on_prem",
|
||||
"condition": {
|
||||
"field": "isCloud",
|
||||
"operator": "!=",
|
||||
"value": true
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"field": "isCloud",
|
||||
"operator": "!=",
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"field": "engineEditionId",
|
||||
"operator": "!=",
|
||||
"value": "11"
|
||||
}
|
||||
],
|
||||
"databaseProperties": [
|
||||
{
|
||||
"displayName": "%onprem.databaseProperties.recoveryModel%",
|
||||
@@ -362,11 +369,13 @@
|
||||
},
|
||||
{
|
||||
"flavor": "cloud",
|
||||
"condition": {
|
||||
"field": "isCloud",
|
||||
"operator": "==",
|
||||
"value": true
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"field": "isCloud",
|
||||
"operator": "==",
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"databaseProperties": [
|
||||
{
|
||||
"displayName": "%cloud.databaseProperties.azureEdition%",
|
||||
@@ -395,6 +404,36 @@
|
||||
"value": "serverEdition"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"flavor": "on_demand",
|
||||
"conditions": [
|
||||
{
|
||||
"field": "engineEditionId",
|
||||
"operator": "==",
|
||||
"value": "11"
|
||||
}
|
||||
],
|
||||
"databaseProperties": [
|
||||
{
|
||||
"displayName": "%cloud.databaseProperties.compatibilityLevel%",
|
||||
"value": "compatibilityLevel"
|
||||
},
|
||||
{
|
||||
"displayName": "%cloud.databaseProperties.owner%",
|
||||
"value": "owner"
|
||||
}
|
||||
],
|
||||
"serverProperties": [
|
||||
{
|
||||
"displayName": "%cloud.serverProperties.serverVersion%",
|
||||
"value": "serverVersion"
|
||||
},
|
||||
{
|
||||
"displayName": "%cloud.serverProperties.serverEdition%",
|
||||
"value": "serverEdition"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -435,7 +474,6 @@
|
||||
},
|
||||
{
|
||||
"name": "%title.books%",
|
||||
"when": "notebookQuality != stable",
|
||||
"row": 0,
|
||||
"col": 2,
|
||||
"colspan": 1,
|
||||
|
||||
@@ -22,7 +22,8 @@ export enum ContextKeys {
|
||||
|
||||
const isCloudEditions = [
|
||||
5,
|
||||
6
|
||||
6,
|
||||
11
|
||||
];
|
||||
|
||||
export function setCommandContext(key: ContextKeys | string, value: any) {
|
||||
|
||||
@@ -73,10 +73,10 @@ export class WebHDFS {
|
||||
*/
|
||||
private getOperationEndpoint(operation: string, path: string, params?: object): string {
|
||||
let endpoint = this._url;
|
||||
endpoint.pathname = this._opts.path + path;
|
||||
endpoint.pathname = encodeURI(this._opts.path + path);
|
||||
let searchOpts = Object.assign(
|
||||
{ 'op': operation },
|
||||
// this._opts.user ? { 'user.name': this._opts.user } : {},
|
||||
this._opts.user ? { 'user.name': this._opts.user } : {},
|
||||
params || {}
|
||||
);
|
||||
endpoint.search = querystring.stringify(searchOpts);
|
||||
@@ -780,10 +780,6 @@ export class WebHDFS {
|
||||
stream.pipe(upload);
|
||||
stream.resume();
|
||||
}
|
||||
if (error && !response) {
|
||||
// request failed, and req is not accessible in this case.
|
||||
throw this.parseError(undefined, undefined, error);
|
||||
}
|
||||
if (error || this.isError(response)) {
|
||||
emitError(req, this.parseError(response, body, error));
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export const groupLabel = localize('mssql.groupLabel', "Group");
|
||||
export const accessHeader = localize('mssql.accessHeader', "Access");
|
||||
export const defaultHeader = localize('mssql.defaultHeader', "Default");
|
||||
export const deleteTitle = localize('mssql.delete', "Delete");
|
||||
export const stickyLabel = localize('mssql.stickyHeader', "Sticky");
|
||||
export const stickyLabel = localize('mssql.stickyHeader', "Sticky Bit");
|
||||
export const inheritDefaultsLabel = localize('mssql.inheritDefaultsLabel', "Inherit Defaults");
|
||||
export const readHeader = localize('mssql.readHeader', "Read");
|
||||
export const writeHeader = localize('mssql.writeHeader', "Write");
|
||||
|
||||
8
extensions/mssql/src/mssql.d.ts
vendored
8
extensions/mssql/src/mssql.d.ts
vendored
@@ -75,6 +75,11 @@ export interface SchemaCompareResult extends azdata.ResultStatus {
|
||||
differences: DiffEntry[];
|
||||
}
|
||||
|
||||
export interface SchemaCompareIncludeExcludeResult extends azdata.ResultStatus {
|
||||
affectedDependencies: DiffEntry[];
|
||||
blockingDependencies: DiffEntry[];
|
||||
}
|
||||
|
||||
export interface SchemaCompareCompletionResult extends azdata.ResultStatus {
|
||||
operationId: string;
|
||||
areEqual: boolean;
|
||||
@@ -91,6 +96,7 @@ export interface DiffEntry {
|
||||
children: DiffEntry[];
|
||||
sourceScript: string;
|
||||
targetScript: string;
|
||||
included: boolean;
|
||||
}
|
||||
|
||||
export const enum SchemaUpdateAction {
|
||||
@@ -290,7 +296,7 @@ export interface ISchemaCompareService {
|
||||
schemaCompareGenerateScript(operationId: string, targetServerName: string, targetDatabaseName: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable<azdata.ResultStatus>;
|
||||
schemaComparePublishChanges(operationId: string, targetServerName: string, targetDatabaseName: string, taskExecutionMode: azdata.TaskExecutionMode): Thenable<azdata.ResultStatus>;
|
||||
schemaCompareGetDefaultOptions(): Thenable<SchemaCompareOptionsResult>;
|
||||
schemaCompareIncludeExcludeNode(operationId: string, diffEntry: DiffEntry, IncludeRequest: boolean, taskExecutionMode: azdata.TaskExecutionMode): Thenable<azdata.ResultStatus>;
|
||||
schemaCompareIncludeExcludeNode(operationId: string, diffEntry: DiffEntry, IncludeRequest: boolean, taskExecutionMode: azdata.TaskExecutionMode): Thenable<SchemaCompareIncludeExcludeResult>;
|
||||
schemaCompareOpenScmp(filePath: string): Thenable<SchemaCompareOpenScmpResult>;
|
||||
schemaCompareSaveScmp(sourceEndpointInfo: SchemaCompareEndpointInfo, targetEndpointInfo: SchemaCompareEndpointInfo, taskExecutionMode: azdata.TaskExecutionMode, deploymentOptions: DeploymentOptions, scmpFilePath: string, excludedSourceObjects: SchemaCompareObjectId[], excludedTargetObjects: SchemaCompareObjectId[]): Thenable<azdata.ResultStatus>;
|
||||
schemaCompareCancel(operationId: string): Thenable<azdata.ResultStatus>;
|
||||
|
||||
@@ -18,6 +18,7 @@ import { WebHDFS, HdfsError } from '../hdfs/webhdfs';
|
||||
import { PermissionStatus } from '../hdfs/aclEntry';
|
||||
import { Mount, MountStatus } from '../hdfs/mount';
|
||||
import { FileStatus, hdfsFileTypeToFileType } from '../hdfs/fileStatus';
|
||||
import { getIgnoreSslVerificationConfigSetting } from '../util/auth';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -143,12 +144,11 @@ export class FileSourceFactory {
|
||||
options = options && options.host ? FileSourceFactory.removePortFromHost(options) : options;
|
||||
let requestParams: IRequestParams = options.requestParams ? options.requestParams : {};
|
||||
if (requestParams.auth || requestParams.isKerberos) {
|
||||
// TODO Remove handling of unsigned cert once we have real certs in our Knox service
|
||||
let agentOptions = {
|
||||
host: options.host,
|
||||
port: options.port,
|
||||
path: constants.hdfsRootPath,
|
||||
rejectUnauthorized: false
|
||||
rejectUnauthorized: !getIgnoreSslVerificationConfigSetting()
|
||||
};
|
||||
let agent = new https.Agent(agentOptions);
|
||||
requestParams['agent'] = agent;
|
||||
|
||||
@@ -76,7 +76,7 @@ export class SchemaCompareService implements mssql.ISchemaCompareService {
|
||||
);
|
||||
}
|
||||
|
||||
public schemaCompareIncludeExcludeNode(operationId: string, diffEntry: mssql.DiffEntry, includeRequest: boolean, taskExecutionMode: azdata.TaskExecutionMode): Thenable<azdata.ResultStatus> {
|
||||
public schemaCompareIncludeExcludeNode(operationId: string, diffEntry: mssql.DiffEntry, includeRequest: boolean, taskExecutionMode: azdata.TaskExecutionMode): Thenable<mssql.SchemaCompareIncludeExcludeResult> {
|
||||
const params: contracts.SchemaCompareNodeParams = { operationId: operationId, diffEntry, includeRequest, taskExecutionMode: taskExecutionMode };
|
||||
return this.client.sendRequest(contracts.SchemaCompareIncludeExcludeNodeRequest.type, params).then(
|
||||
undefined,
|
||||
|
||||
@@ -37,8 +37,7 @@ export class SparkJobSubmissionService {
|
||||
uri: livyUrl,
|
||||
method: 'POST',
|
||||
json: true,
|
||||
// TODO, change it back after service's authentication changed.
|
||||
rejectUnauthorized: false,
|
||||
rejectUnauthorized: !auth.getIgnoreSslVerificationConfigSetting(),
|
||||
body: {
|
||||
file: submissionArgs.sparkFile,
|
||||
proxyUser: submissionArgs.user,
|
||||
@@ -114,7 +113,7 @@ export class SparkJobSubmissionService {
|
||||
uri: livyUrl,
|
||||
method: 'GET',
|
||||
json: true,
|
||||
rejectUnauthorized: false,
|
||||
rejectUnauthorized: !auth.getIgnoreSslVerificationConfigSetting(),
|
||||
// authentication headers
|
||||
headers: headers
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as kerberos from 'kerberos';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export enum AuthType {
|
||||
Integrated = 'integrated',
|
||||
@@ -17,3 +18,21 @@ export async function authenticateKerberos(hostname: string): Promise<string> {
|
||||
let response = await client.step('');
|
||||
return response;
|
||||
}
|
||||
|
||||
const bdcConfigSectionName = 'bigDataCluster';
|
||||
const ignoreSslConfigName = 'ignoreSslVerification';
|
||||
|
||||
/**
|
||||
* Retrieves the current setting for whether to ignore SSL verification errors
|
||||
*/
|
||||
export function getIgnoreSslVerificationConfigSetting(): boolean {
|
||||
try {
|
||||
const config = vscode.workspace.getConfiguration(bdcConfigSectionName);
|
||||
const value = config.get<boolean>(ignoreSslConfigName);
|
||||
return value !== undefined ? value : true;
|
||||
} catch (error) {
|
||||
console.error(`Unexpected error retrieving ${bdcConfigSectionName}.${ignoreSslConfigName} setting : ${error}`);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -140,9 +140,15 @@
|
||||
"title": "%title.SQL19PreviewBook%",
|
||||
"category": "%books-preview-category%"
|
||||
},
|
||||
{
|
||||
"command": "books.command.openLocalizedBooks",
|
||||
"title": "%title.PreviewLocalizedBook%",
|
||||
"category": "%books-preview-category%"
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.saveBook",
|
||||
"title": "%title.saveJupyterBook%",
|
||||
"category": "%books-preview-category%",
|
||||
"icon": {
|
||||
"dark": "resources/dark/save_inverse.svg",
|
||||
"light": "resources/light/save.svg"
|
||||
@@ -151,6 +157,16 @@
|
||||
{
|
||||
"command": "notebook.command.searchBook",
|
||||
"title": "%title.searchJupyterBook%",
|
||||
"category": "%books-preview-category%",
|
||||
"icon": {
|
||||
"dark": "resources/dark/search_inverse.svg",
|
||||
"light": "resources/light/search.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.searchUntitledBook",
|
||||
"title": "%title.searchJupyterBook%",
|
||||
"category": "%books-preview-category%",
|
||||
"icon": {
|
||||
"dark": "resources/dark/search_inverse.svg",
|
||||
"light": "resources/light/search.svg"
|
||||
@@ -235,7 +251,19 @@
|
||||
},
|
||||
{
|
||||
"command": "books.sqlserver2019",
|
||||
"when": "sqlserver2019 && notebookQuality != stable"
|
||||
"when": "sqlserver2019"
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.saveBook",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.searchBook",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.searchUntitledBook",
|
||||
"when": "false"
|
||||
}
|
||||
],
|
||||
"touchBar": [
|
||||
@@ -270,12 +298,17 @@
|
||||
"view/item/context": [
|
||||
{
|
||||
"command": "notebook.command.searchBook",
|
||||
"when": "view =~ /^(untitledBookTreeView|bookTreeView)$/ && viewItem =~ /^(untitledBook|savedBook)$/ && notebookQuality != stable",
|
||||
"when": "view == bookTreeView && viewItem == savedBook",
|
||||
"group": "inline"
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.searchUntitledBook",
|
||||
"when": "view == unsavedBookTreeView && viewItem == unsavedBook && unsavedBooks",
|
||||
"group": "inline"
|
||||
},
|
||||
{
|
||||
"command": "notebook.command.saveBook",
|
||||
"when": "view == untitledBookTreeView && viewItem == untitledBook && untitledBooks && notebookQuality != stable",
|
||||
"when": "view == unsavedBookTreeView && viewItem == unsavedBook && unsavedBooks",
|
||||
"group": "inline"
|
||||
}
|
||||
],
|
||||
@@ -397,8 +430,8 @@
|
||||
"name": "%title.SavedBooks%"
|
||||
},
|
||||
{
|
||||
"id": "untitledBookTreeView",
|
||||
"name": "%title.UntitledBooks%"
|
||||
"id": "unsavedBookTreeView",
|
||||
"name": "%title.UnsavedBooks%"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -32,5 +32,6 @@
|
||||
"title.saveJupyterBook": "Save Book",
|
||||
"title.searchJupyterBook": "Search Book",
|
||||
"title.SavedBooks": "Saved Books",
|
||||
"title.UntitledBooks": "Untitled Books"
|
||||
"title.UnsavedBooks": "Unsaved Books",
|
||||
"title.PreviewLocalizedBook": "Get localized SQL Server 2019 guide"
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"url": "",
|
||||
"auth": "None"
|
||||
},
|
||||
"ignore_ssl_errors": true,
|
||||
"livy_session_startup_timeout_seconds": 100,
|
||||
"logging_config": {
|
||||
"version": 1,
|
||||
"formatters": {
|
||||
@@ -37,4 +37,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export class BookTreeItem extends vscode.TreeItem {
|
||||
this.collapsibleState = book.treeItemCollapsibleState;
|
||||
this._sections = book.page;
|
||||
if (book.isUntitled) {
|
||||
this.contextValue = 'untitledBook';
|
||||
this.contextValue = 'unsavedBook';
|
||||
} else {
|
||||
this.contextValue = 'savedBook';
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
}
|
||||
|
||||
private async initialize(bookPaths: string[]): Promise<void> {
|
||||
await vscode.commands.executeCommand('setContext', 'untitledBooks', this._openAsUntitled);
|
||||
await vscode.commands.executeCommand('setContext', 'unsavedBooks', this._openAsUntitled);
|
||||
await Promise.all(bookPaths.map(async (bookPath) => {
|
||||
let book: BookModel = new BookModel(bookPath, this._openAsUntitled, this._extensionContext);
|
||||
await book.initializeContents();
|
||||
@@ -203,8 +203,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
private runThrottledAction(resource: string, action: () => void) {
|
||||
const isResourceChange = resource !== this._resource;
|
||||
if (isResourceChange) {
|
||||
clearTimeout(this._throttleTimer);
|
||||
this._throttleTimer = undefined;
|
||||
this.clearAndResetThrottleTimer();
|
||||
}
|
||||
|
||||
this._resource = resource;
|
||||
@@ -214,11 +213,19 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
if (isResourceChange) {
|
||||
action();
|
||||
} else {
|
||||
this._throttleTimer = setTimeout(() => action(), 300);
|
||||
this._throttleTimer = setTimeout(() => {
|
||||
action();
|
||||
this.clearAndResetThrottleTimer();
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private clearAndResetThrottleTimer(): void {
|
||||
clearTimeout(this._throttleTimer);
|
||||
this._throttleTimer = undefined;
|
||||
}
|
||||
|
||||
openExternalLink(resource: string): void {
|
||||
try {
|
||||
vscode.env.openExternal(vscode.Uri.parse(resource));
|
||||
|
||||
@@ -66,20 +66,29 @@ export function executeStreamedCommand(cmd: string, options: childProcess.SpawnO
|
||||
let child = childProcess.spawn(cmd, [], options);
|
||||
|
||||
// Add listeners to resolve/reject the promise on exit
|
||||
child.on('error', reject);
|
||||
child.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
let stdErrLog = '';
|
||||
child.on('exit', (code: number) => {
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(localize('executeCommandProcessExited', 'Process exited with code {0}', code));
|
||||
reject(localize('executeCommandProcessExited', 'Process exited with with error code: {0}. StdErr Output: {1}', code, stdErrLog));
|
||||
}
|
||||
});
|
||||
|
||||
// Add listeners to print stdout and stderr if an output channel was provided
|
||||
if (outputChannel) {
|
||||
child.stdout.on('data', data => { outputDataChunk(data, outputChannel, ' stdout: '); });
|
||||
child.stderr.on('data', data => { outputDataChunk(data, outputChannel, ' stderr: '); });
|
||||
}
|
||||
child.stderr.on('data', data => {
|
||||
if (outputChannel) {
|
||||
outputDataChunk(data, outputChannel, ' stderr: ');
|
||||
}
|
||||
stdErrLog += data.toString();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -237,3 +246,20 @@ export async function exists(path: string): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const bdcConfigSectionName = 'bigDataCluster';
|
||||
const ignoreSslConfigName = 'ignoreSslVerification';
|
||||
|
||||
/**
|
||||
* Retrieves the current setting for whether to ignore SSL verification errors
|
||||
*/
|
||||
export function getIgnoreSslVerificationConfigSetting(): boolean {
|
||||
try {
|
||||
const config = vscode.workspace.getConfiguration(bdcConfigSectionName);
|
||||
const value = config.get<boolean>(ignoreSslConfigName);
|
||||
return value !== undefined ? value : true;
|
||||
} catch (error) {
|
||||
console.error(`Unexpected error retrieving ${bdcConfigSectionName}.${ignoreSslConfigName} setting : ${error}`);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ const JUPYTER_NOTEBOOK_PROVIDER = 'jupyter';
|
||||
const msgSampleCodeDataFrame = localize('msgSampleCodeDataFrame', "This sample code loads the file into a data frame and shows the first 10 results.");
|
||||
const noNotebookVisible = localize('noNotebookVisible', "No notebook editor is active");
|
||||
const BOOKS_VIEWID = 'bookTreeView';
|
||||
const READONLY_BOOKS_VIEWID = 'untitledBookTreeView';
|
||||
const READONLY_BOOKS_VIEWID = 'unsavedBookTreeView';
|
||||
let controller: JupyterController;
|
||||
type ChooseCellType = { label: string, id: CellType };
|
||||
|
||||
@@ -35,6 +35,7 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openExternalLink', (resource) => bookTreeViewProvider.openExternalLink(resource)));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.saveBook', () => untitledBookTreeViewProvider.saveJupyterBooks()));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.searchBook', () => bookTreeViewProvider.searchJupyterBooks()));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.searchUntitledBook', () => untitledBookTreeViewProvider.searchJupyterBooks()));
|
||||
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('_notebook.command.new', (context?: azdata.ConnectedContext) => {
|
||||
let connectionProfile: azdata.IConnectionProfile = undefined;
|
||||
@@ -43,17 +44,17 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
|
||||
}
|
||||
newNotebook(connectionProfile);
|
||||
}));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.open', () => {
|
||||
openNotebook();
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.open', async () => {
|
||||
await openNotebook();
|
||||
}));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.runactivecell', () => {
|
||||
runActiveCell();
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.runactivecell', async () => {
|
||||
await runActiveCell();
|
||||
}));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.runallcells', () => {
|
||||
runAllCells();
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.runallcells', async () => {
|
||||
await runAllCells();
|
||||
}));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.clearactivecellresult', () => {
|
||||
clearActiveCellOutput();
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.clearactivecellresult', async () => {
|
||||
await clearActiveCellOutput();
|
||||
}));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.addcell', async () => {
|
||||
let cellType: CellType;
|
||||
@@ -76,20 +77,24 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi
|
||||
return;
|
||||
}
|
||||
if (cellType) {
|
||||
addCell(cellType);
|
||||
await addCell(cellType);
|
||||
}
|
||||
}));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.addcode', () => {
|
||||
addCell('code');
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.addcode', async () => {
|
||||
await addCell('code');
|
||||
}));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.addtext', () => {
|
||||
addCell('markdown');
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.addtext', async () => {
|
||||
await addCell('markdown');
|
||||
}));
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.analyzeNotebook', (explorerContext: azdata.ObjectExplorerContext) => {
|
||||
analyzeNotebook(explorerContext);
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.analyzeNotebook', async (explorerContext: azdata.ObjectExplorerContext) => {
|
||||
await analyzeNotebook(explorerContext);
|
||||
}));
|
||||
extensionContext.subscriptions.push(vscode.window.registerUriHandler(new NotebookUriHandler()));
|
||||
|
||||
extensionContext.subscriptions.push(vscode.commands.registerCommand('books.command.openLocalizedBooks', async () => {
|
||||
const urlToOpen: string = 'https://aka.ms/localized-BDC-book';
|
||||
await vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(urlToOpen));
|
||||
}));
|
||||
|
||||
let appContext = new AppContext(extensionContext, new ApiWrapper());
|
||||
controller = new JupyterController(appContext);
|
||||
|
||||
@@ -87,6 +87,8 @@ export class JupyterKernel implements nb.IKernel {
|
||||
}
|
||||
|
||||
requestExecute(content: nb.IExecuteRequest, disposeOnDone?: boolean): nb.IFuture {
|
||||
content.code = Array.isArray(content.code) ? content.code.join('') : content.code;
|
||||
content.code = content.code.replace(/\r+\n/gm, '\n'); // Remove \r (if it exists) from newlines
|
||||
let futureImpl = this.kernelImpl.requestExecute(content as KernelMessage.IExecuteRequest, disposeOnDone);
|
||||
return new JupyterFuture(futureImpl);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ export class JupyterNotebookManager implements nb.NotebookManager, vscode.Dispos
|
||||
private _sessionManager: JupyterSessionManager;
|
||||
|
||||
constructor(private _serverManager: LocalJupyterServerManager, sessionManager?: JupyterSessionManager, private apiWrapper: ApiWrapper = new ApiWrapper()) {
|
||||
this._sessionManager = sessionManager || new JupyterSessionManager();
|
||||
let pythonEnvVarPath = this._serverManager && this._serverManager.jupyterServerInstallation && this._serverManager.jupyterServerInstallation.pythonEnvVarPath;
|
||||
this._sessionManager = sessionManager || new JupyterSessionManager(pythonEnvVarPath);
|
||||
this._serverManager.onServerStarted(() => {
|
||||
this.setServerSettings(this._serverManager.serverSettings);
|
||||
});
|
||||
|
||||
@@ -36,6 +36,7 @@ const msgSkipPythonInstall = localize('msgSkipPythonInstall', "Python already ex
|
||||
const msgWaitingForInstall = localize('msgWaitingForInstall', "Another Python installation is currently in progress. Waiting for it to complete.");
|
||||
function msgDependenciesInstallationFailed(errorMessage: string): string { return localize('msgDependenciesInstallationFailed', "Installing Notebook dependencies failed with error: {0}", errorMessage); }
|
||||
function msgDownloadPython(platform: string, pythonDownloadUrl: string): string { return localize('msgDownloadPython', "Downloading local python for platform: {0} to {1}", platform, pythonDownloadUrl); }
|
||||
function msgPackageRetrievalFailed(errorMessage: string): string { return localize('msgPackageRetrievalFailed', "Encountered an error when trying to retrieve list of installed packages: {0}", errorMessage); }
|
||||
|
||||
export class JupyterServerInstallation {
|
||||
public apiWrapper: ApiWrapper;
|
||||
@@ -77,7 +78,7 @@ export class JupyterServerInstallation {
|
||||
version: '1.3.0'
|
||||
}, {
|
||||
name: 'powershell-kernel',
|
||||
version: '0.1.0'
|
||||
version: '0.1.1'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -519,14 +520,19 @@ export class JupyterServerInstallation {
|
||||
}
|
||||
|
||||
public async getInstalledPipPackages(): Promise<PythonPkgDetails[]> {
|
||||
let cmd = `"${this.pythonExecutable}" -m pip list --format=json`;
|
||||
let packagesInfo = await this.executeBufferedCommand(cmd);
|
||||
|
||||
let packagesResult: PythonPkgDetails[] = [];
|
||||
if (packagesInfo) {
|
||||
packagesResult = <PythonPkgDetails[]>JSON.parse(packagesInfo);
|
||||
try {
|
||||
let cmd = `"${this.pythonExecutable}" -m pip list --format=json`;
|
||||
let packagesInfo = await this.executeBufferedCommand(cmd);
|
||||
let packagesResult: PythonPkgDetails[] = [];
|
||||
if (packagesInfo) {
|
||||
packagesResult = <PythonPkgDetails[]>JSON.parse(packagesInfo);
|
||||
}
|
||||
return packagesResult;
|
||||
}
|
||||
catch (err) {
|
||||
this.outputChannel.appendLine(msgPackageRetrievalFailed(utils.getErrorMessage(err)));
|
||||
return [];
|
||||
}
|
||||
return packagesResult;
|
||||
}
|
||||
|
||||
public installPipPackages(packages: PythonPkgDetails[], useMinVersion: boolean): Promise<void> {
|
||||
@@ -549,19 +555,25 @@ export class JupyterServerInstallation {
|
||||
}
|
||||
|
||||
public async getInstalledCondaPackages(): Promise<PythonPkgDetails[]> {
|
||||
let condaExe = this.getCondaExePath();
|
||||
let cmd = `"${condaExe}" list --json`;
|
||||
let packagesInfo = await this.executeBufferedCommand(cmd);
|
||||
try {
|
||||
let condaExe = this.getCondaExePath();
|
||||
let cmd = `"${condaExe}" list --json`;
|
||||
let packagesInfo = await this.executeBufferedCommand(cmd);
|
||||
|
||||
if (packagesInfo) {
|
||||
let packagesResult = JSON.parse(packagesInfo);
|
||||
if (Array.isArray(packagesResult)) {
|
||||
return packagesResult
|
||||
.filter(pkg => pkg && pkg.channel && pkg.channel !== 'pypi')
|
||||
.map(pkg => <PythonPkgDetails>{ name: pkg.name, version: pkg.version });
|
||||
if (packagesInfo) {
|
||||
let packagesResult = JSON.parse(packagesInfo);
|
||||
if (Array.isArray(packagesResult)) {
|
||||
return packagesResult
|
||||
.filter(pkg => pkg && pkg.channel && pkg.channel !== 'pypi')
|
||||
.map(pkg => <PythonPkgDetails>{ name: pkg.name, version: pkg.version });
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
catch (err) {
|
||||
this.outputChannel.appendLine(msgPackageRetrievalFailed(utils.getErrorMessage(err)));
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
public installCondaPackages(packages: PythonPkgDetails[], useMinVersion: boolean): Promise<void> {
|
||||
|
||||
@@ -54,6 +54,10 @@ export class LocalJupyterServerManager implements nb.ServerManager, vscode.Dispo
|
||||
return this._onServerStarted.event;
|
||||
}
|
||||
|
||||
public get jupyterServerInstallation(): JupyterServerInstallation | undefined {
|
||||
return this.options && this.options.jupyterInstallation;
|
||||
}
|
||||
|
||||
public async startServer(): Promise<void> {
|
||||
try {
|
||||
if (!this._jupyterServer) {
|
||||
@@ -80,6 +84,7 @@ export class LocalJupyterServerManager implements nb.ServerManager, vscode.Dispo
|
||||
public async stopServer(): Promise<void> {
|
||||
if (this._jupyterServer) {
|
||||
await this._jupyterServer.stop();
|
||||
this._jupyterServer = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,9 +26,6 @@ const configBase = {
|
||||
'kernel_r_credentials': {
|
||||
'url': ''
|
||||
},
|
||||
|
||||
'ignore_ssl_errors': true,
|
||||
|
||||
'logging_config': {
|
||||
'version': 1,
|
||||
'formatters': {
|
||||
@@ -69,7 +66,7 @@ export class JupyterSessionManager implements nb.SessionManager {
|
||||
private _sessionManager: Session.IManager;
|
||||
private static _sessions: JupyterSession[] = [];
|
||||
|
||||
constructor() {
|
||||
constructor(private _pythonEnvVarPath?: string) {
|
||||
this._isReady = false;
|
||||
this._ready = new Deferred<void>();
|
||||
}
|
||||
@@ -123,7 +120,7 @@ export class JupyterSessionManager implements nb.SessionManager {
|
||||
return Promise.reject(new Error(localize('errorStartBeforeReady', 'Cannot start a session, the manager is not yet initialized')));
|
||||
}
|
||||
let sessionImpl = await this._sessionManager.startNew(options);
|
||||
let jupyterSession = new JupyterSession(sessionImpl, skipSettingEnvironmentVars);
|
||||
let jupyterSession = new JupyterSession(sessionImpl, skipSettingEnvironmentVars, this._pythonEnvVarPath);
|
||||
await jupyterSession.messagesComplete;
|
||||
let index = JupyterSessionManager._sessions.findIndex(session => session.path === options.path);
|
||||
if (index > -1) {
|
||||
@@ -170,8 +167,13 @@ export class JupyterSession implements nb.ISession {
|
||||
private _kernel: nb.IKernel;
|
||||
private _messagesComplete: Deferred<void> = new Deferred<void>();
|
||||
|
||||
constructor(private sessionImpl: Session.ISession, skipSettingEnvironmentVars?: boolean) {
|
||||
this.setEnvironmentVars(skipSettingEnvironmentVars);
|
||||
constructor(private sessionImpl: Session.ISession, skipSettingEnvironmentVars?: boolean, private _pythonEnvVarPath?: string) {
|
||||
this.setEnvironmentVars(skipSettingEnvironmentVars).catch(error => {
|
||||
console.error(`Unexpected exception setting Jupyter Session variables : ${error}`);
|
||||
// We don't want callers to hang forever waiting - it's better to continue on even if we weren't
|
||||
// able to set environment variables
|
||||
this._messagesComplete.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
public get canChangeKernels(): boolean {
|
||||
@@ -308,6 +310,7 @@ export class JupyterSession implements nb.ISession {
|
||||
config.kernel_scala_credentials = creds;
|
||||
config.kernel_r_credentials = creds;
|
||||
config.logging_config.handlers.magicsHandler.home_path = homePath;
|
||||
config.ignore_ssl_errors = utils.getIgnoreSslVerificationConfigSetting();
|
||||
}
|
||||
|
||||
private getKnoxPortOrDefault(connectionProfile: IConnectionProfile): string {
|
||||
@@ -333,13 +336,23 @@ export class JupyterSession implements nb.ISession {
|
||||
private async setEnvironmentVars(skip: boolean = false): Promise<void> {
|
||||
if (!skip && this.sessionImpl) {
|
||||
let allCode: string = '';
|
||||
// Ensure cwd matches notebook path (this follows Jupyter behavior)
|
||||
if (this.path && path.dirname(this.path)) {
|
||||
allCode += `%cd ${path.dirname(this.path)}${EOL}`;
|
||||
}
|
||||
for (let i = 0; i < Object.keys(process.env).length; i++) {
|
||||
let key = Object.keys(process.env)[i];
|
||||
// Jupyter doesn't seem to alow for setting multiple variables at once, so doing it with multiple commands
|
||||
allCode += `%set_env ${key}=${process.env[key]}${EOL}`;
|
||||
if (key.toLowerCase() === 'path' && this._pythonEnvVarPath) {
|
||||
allCode += `%set_env ${key}=${this._pythonEnvVarPath}${EOL}`;
|
||||
} else {
|
||||
// Jupyter doesn't seem to alow for setting multiple variables at once, so doing it with multiple commands
|
||||
allCode += `%set_env ${key}=${process.env[key]}${EOL}`;
|
||||
}
|
||||
}
|
||||
let future = this.sessionImpl.kernel.requestExecute({
|
||||
code: allCode
|
||||
code: allCode,
|
||||
silent: true,
|
||||
store_history: false
|
||||
}, true);
|
||||
await future.done;
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ export class PerFolderServerInstance implements IServerInstance {
|
||||
let onErrorBeforeStartup = (err: any) => reject(err);
|
||||
let onExitBeforeStart = (err: any) => {
|
||||
if (!this.isStarted) {
|
||||
reject(localize('notebookStartProcessExitPremature', 'Notebook process exited prematurely with error: {0}, StdErr Output: {1}', err, stdErrLog));
|
||||
reject(localize('notebookStartProcessExitPremature', 'Notebook process exited prematurely with error code: {0}. StdErr Output: {1}', err, stdErrLog));
|
||||
}
|
||||
};
|
||||
this.childProcess.on('error', onErrorBeforeStartup);
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
"objectExplorer/item/context": [
|
||||
{
|
||||
"command": "profiler.newProfiler",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Server",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Server && mssql:engineedition != 11",
|
||||
"group": "profiler"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,273 +0,0 @@
|
||||
{
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"name": "python3",
|
||||
"display_name": "Python 3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"version": "3.6.6",
|
||||
"mimetype": "text/x-python",
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"pygments_lexer": "ipython3",
|
||||
"nbconvert_exporter": "python",
|
||||
"file_extension": ".py"
|
||||
}
|
||||
},
|
||||
"nbformat_minor": 2,
|
||||
"nbformat": 4,
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"\n",
|
||||
" \n",
|
||||
"## Create Azure Kubernetes Service cluster and deploy SQL Server 2019 Big Data Cluster\n",
|
||||
" \n",
|
||||
"This notebook walks through the process of creating a new Azure Kubernetes Service cluster first, and then deploys a <a href=\"https://docs.microsoft.com/sql/big-data-cluster/big-data-cluster-overview?view=sqlallproducts-allversions\">SQL Server 2019 Big Data Cluster</a> on the newly created AKS cluster."
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "4f6bc3bc-3592-420a-b534-384011189005"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import json,sys,os\n",
|
||||
"def run_command(command):\n",
|
||||
" print(\"Executing: \" + command)\n",
|
||||
" !{command}\n",
|
||||
" if _exit_code != 0:\n",
|
||||
" sys.exit(f'Command execution failed with exit code: {str(_exit_code)}.\\n\\t{command}\\n')\n",
|
||||
" print(f'Successfully executed: {command}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "326645cf-022a-47f2-8aff-37de71da8955",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 1
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Set variables**\n",
|
||||
"Generated by Azure Data Studio using the values collected in the Deploy Big Data Cluster wizard"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "8716915b-1439-431b-ab0a-0221ef94cb7f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Set password**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "b083aa8d-990c-4170-ba1d-247ba5c6ae76"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"mssql_password = os.environ[\"AZDATA_NB_VAR_BDC_ADMIN_PASSWORD\"]"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "de256ddd-b835-4eb6-8cfc-c1a6239b0726"
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 0
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Login to Azure**\n",
|
||||
"\n",
|
||||
"This will open a web browser window to enable credentials to be entered. If this cells is hanging forever, it might be because your Web browser windows is waiting for you to enter your Azure credentials!\n",
|
||||
""
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "baddf2d9-93ee-4c42-aaf1-b42116bb1912"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"run_command(f'az login')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "8f1404a6-216d-49fb-b6ad-81beeea50083",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 5
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"\n",
|
||||
"### **Set active Azure subscription**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "230dc0f1-bf6e-474a-bfaa-aae6f8aad12e"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"if azure_subscription_id != \"\":\n",
|
||||
" run_command(f'az account set --subscription {azure_subscription_id}')\n",
|
||||
"else:\n",
|
||||
" print('Using the default Azure subscription', {azure_subscription_id})\n",
|
||||
"run_command(f'az account show')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "ab230931-2e99-483b-a229-3847684a8c1c",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 6
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Create Azure resource group**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "d51db914-f484-489f-990d-72edb3065068"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"run_command(f'az group create --name {azure_resource_group} --location {azure_region}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "7c53eb23-c327-41bf-8936-bd34a02ebdd5",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 7
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Create AKS cluster**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "818eb705-71e2-4013-8420-44886a5468b2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"run_command(f'az aks create --name {aks_cluster_name} --resource-group {azure_resource_group} --generate-ssh-keys --node-vm-size {azure_vm_size} --node-count {azure_vm_count}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "3cea1da0-0c18-4030-a5aa-79bc98a5a14d",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 8
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Set the new AKS cluster as current context**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "5ade8453-5e71-478f-b6b6-83c55626243d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"run_command(f'az aks get-credentials --resource-group {azure_resource_group} --name {aks_cluster_name} --admin --overwrite-existing')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "9ccb9adf-1cf6-4dcb-8bd9-7ae9a85c2437",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 9
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Create deployment configuration files**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "57eb69fb-c68f-4ba8-818d-ffbaa0bc7aec"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"mssql_target_profile = 'ads-bdc-custom-profile'\n",
|
||||
"if not os.path.exists(mssql_target_profile):\n",
|
||||
" os.mkdir(mssql_target_profile)\n",
|
||||
"bdcJsonObj = json.loads(bdc_json)\n",
|
||||
"controlJsonObj = json.loads(control_json)\n",
|
||||
"bdcJsonFile = open(f'{mssql_target_profile}/bdc.json', 'w')\n",
|
||||
"bdcJsonFile.write(json.dumps(bdcJsonObj, indent = 4))\n",
|
||||
"bdcJsonFile.close()\n",
|
||||
"controlJsonFile = open(f'{mssql_target_profile}/control.json', 'w')\n",
|
||||
"controlJsonFile.write(json.dumps(controlJsonObj, indent = 4))\n",
|
||||
"controlJsonFile.close()\n",
|
||||
"print(f'Created deployment configuration folder: {mssql_target_profile}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "3fd73c04-8a79-4d08-9049-1dad30265558",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 10
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Create SQL Server 2019 Big Data Cluster**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "6e82fad8-0fd0-4952-87ce-3fea1edd98cb"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"print (f'Creating SQL Server 2019 Big Data Cluster: {mssql_cluster_name} using configuration {mssql_target_profile}')\n",
|
||||
"os.environ[\"ACCEPT_EULA\"] = 'yes'\n",
|
||||
"os.environ[\"AZDATA_USERNAME\"] = mssql_username\n",
|
||||
"os.environ[\"AZDATA_PASSWORD\"] = mssql_password\n",
|
||||
"if os.name == 'nt':\n",
|
||||
" print(f'If you don\\'t see output produced by azdata, you can run the following command in a terminal window to check the deployment status:\\n\\tkubectl get pods -n {mssql_cluster_name} ')\n",
|
||||
"run_command(f'azdata bdc create -c {mssql_target_profile}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "c43ea026-ca5e-4e2a-8602-fcc786354168",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 11
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
{
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"name": "python3",
|
||||
"display_name": "Python 3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"version": "3.6.6",
|
||||
"mimetype": "text/x-python",
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"pygments_lexer": "ipython3",
|
||||
"nbconvert_exporter": "python",
|
||||
"file_extension": ".py"
|
||||
}
|
||||
},
|
||||
"nbformat_minor": 2,
|
||||
"nbformat": 4,
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"\n",
|
||||
" \n",
|
||||
"## Deploy SQL Server 2019 Big Data Cluster on an existing Azure Kubernetes Service (AKS) cluster\n",
|
||||
" \n",
|
||||
"This notebook walks through the process of deploying a <a href=\"https://docs.microsoft.com/sql/big-data-cluster/big-data-cluster-overview?view=sqlallproducts-allversions\">SQL Server 2019 Big Data Cluster</a> on an existing AKS cluster."
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "82e60c1a-7acf-47ee-877f-9e85e92e11da"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import json,sys,os\n",
|
||||
"def run_command(command):\n",
|
||||
" print(\"Executing: \" + command)\n",
|
||||
" !{command}\n",
|
||||
" if _exit_code != 0:\n",
|
||||
" sys.exit(f'Command execution failed with exit code: {str(_exit_code)}.\\n\\t{command}\\n')\n",
|
||||
" print(f'Successfully executed: {command}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "d973d5b4-7f0a-4a9d-b204-a16480f3940d",
|
||||
"tags": []
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 1
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Set variables**\n",
|
||||
"Generated by Azure Data Studio using the values collected in the Deploy Big Data Cluster wizard"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "4b266b2d-bd1b-4565-92c9-3fc146cdce6d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Set password**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "7c37d248-b9ac-4ad6-be56-158cd70443b1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"mssql_password = os.environ[\"AZDATA_NB_VAR_BDC_ADMIN_PASSWORD\"]"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "83d455f3-db10-48bb-bb81-78a6b4e5f2fd"
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 0
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Set and show current context**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "127c8042-181f-4862-a390-96e59c181d09"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"run_command(f'kubectl config use-context {mssql_cluster_context}')\n",
|
||||
"run_command('kubectl config current-context')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "7d1a03d4-1df8-48eb-bff0-0042603b95b1",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 0
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Create deployment configuration files**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "138536c3-1db6-428f-9e5c-8269a02fb52e"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"mssql_target_profile = 'ads-bdc-custom-profile'\n",
|
||||
"if not os.path.exists(mssql_target_profile):\n",
|
||||
" os.mkdir(mssql_target_profile)\n",
|
||||
"bdcJsonObj = json.loads(bdc_json)\n",
|
||||
"controlJsonObj = json.loads(control_json)\n",
|
||||
"bdcJsonFile = open(f'{mssql_target_profile}/bdc.json', 'w')\n",
|
||||
"bdcJsonFile.write(json.dumps(bdcJsonObj, indent = 4))\n",
|
||||
"bdcJsonFile.close()\n",
|
||||
"controlJsonFile = open(f'{mssql_target_profile}/control.json', 'w')\n",
|
||||
"controlJsonFile.write(json.dumps(controlJsonObj, indent = 4))\n",
|
||||
"controlJsonFile.close()\n",
|
||||
"print(f'Created deployment configuration folder: {mssql_target_profile}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "2ff82c8a-4bce-449c-9d91-3ac7dd272021",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 6
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Create SQL Server 2019 Big Data Cluster**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "efe78cd3-ed73-4c9b-b586-fdd6c07dd37f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"print (f'Creating SQL Server 2019 Big Data Cluster: {mssql_cluster_name} using configuration {mssql_target_profile}')\n",
|
||||
"os.environ[\"ACCEPT_EULA\"] = 'yes'\n",
|
||||
"os.environ[\"AZDATA_USERNAME\"] = mssql_username\n",
|
||||
"os.environ[\"AZDATA_PASSWORD\"] = mssql_password\n",
|
||||
"run_command(f'azdata bdc create -c {mssql_target_profile}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "373947a1-90b9-49ee-86f4-17a4c7d4ca76",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 7
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
{
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"name": "python3",
|
||||
"display_name": "Python 3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"version": "3.6.6",
|
||||
"mimetype": "text/x-python",
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"pygments_lexer": "ipython3",
|
||||
"nbconvert_exporter": "python",
|
||||
"file_extension": ".py"
|
||||
}
|
||||
},
|
||||
"nbformat_minor": 2,
|
||||
"nbformat": 4,
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"\n",
|
||||
" \n",
|
||||
"## Deploy SQL Server 2019 Big Data Cluster on an existing cluster deployed using kubeadm\n",
|
||||
" \n",
|
||||
"This notebook walks through the process of deploying a <a href=\"https://docs.microsoft.com/sql/big-data-cluster/big-data-cluster-overview?view=sqlallproducts-allversions\">SQL Server 2019 Big Data Cluster</a> on an existing kubeadm cluster."
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "23954d96-3932-4a8e-ab73-da605f99b1a4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import json,sys,os\n",
|
||||
"def run_command(command):\n",
|
||||
" print(\"Executing: \" + command)\n",
|
||||
" !{command}\n",
|
||||
" if _exit_code != 0:\n",
|
||||
" sys.exit(f'Command execution failed with exit code: {str(_exit_code)}.\\n\\t{command}\\n')\n",
|
||||
" print(f'Successfully executed: {command}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "26fa8bc4-4b8e-4c31-ae11-50484821cea8",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 1
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Set variables**\n",
|
||||
"Generated by Azure Data Studio using the values collected in the Deploy Big Data Cluster wizard"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "e70640d0-6059-4cab-939e-e985a978c0da"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Set password**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "7b383b0d-5687-45b3-a16f-ba3b170c796e"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"mssql_password = os.environ[\"AZDATA_NB_VAR_BDC_ADMIN_PASSWORD\"]\n",
|
||||
"if mssql_auth_mode == \"ad\":\n",
|
||||
" mssql_domain_service_account_password = os.environ[\"AZDATA_NB_VAR_BDC_AD_DOMAIN_SVC_PASSWORD\"]"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "b5970f2b-cf13-41af-b0a2-5133d840325e",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 3
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Set and show current context**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "6456bd0c-5b64-4d76-be59-e3a5b32697f5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"run_command(f'kubectl config use-context {mssql_cluster_context}')\n",
|
||||
"run_command('kubectl config current-context')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "a38f8b3a-f93a-484c-b9e2-4eba3ed99cc2",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 0
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Create deployment configuration files**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "6d78da36-6af5-4309-baad-bc81bb2cdb7f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"mssql_target_profile = 'ads-bdc-custom-profile'\n",
|
||||
"if not os.path.exists(mssql_target_profile):\n",
|
||||
" os.mkdir(mssql_target_profile)\n",
|
||||
"bdcJsonObj = json.loads(bdc_json)\n",
|
||||
"controlJsonObj = json.loads(control_json)\n",
|
||||
"bdcJsonFile = open(f'{mssql_target_profile}/bdc.json', 'w')\n",
|
||||
"bdcJsonFile.write(json.dumps(bdcJsonObj, indent = 4))\n",
|
||||
"bdcJsonFile.close()\n",
|
||||
"controlJsonFile = open(f'{mssql_target_profile}/control.json', 'w')\n",
|
||||
"controlJsonFile.write(json.dumps(controlJsonObj, indent = 4))\n",
|
||||
"controlJsonFile.close()\n",
|
||||
"print(f'Created deployment configuration folder: {mssql_target_profile}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "3110ab23-ecfc-4e36-a1c5-28536b7edebf",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 6
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Create SQL Server 2019 Big Data Cluster**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "7d56d262-8cd5-49e4-b745-332c6e7a3cb2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"print (f'Creating SQL Server 2019 Big Data Cluster: {mssql_cluster_name} using configuration {mssql_target_profile}')\n",
|
||||
"os.environ[\"ACCEPT_EULA\"] = 'yes'\n",
|
||||
"os.environ[\"AZDATA_USERNAME\"] = mssql_username\n",
|
||||
"os.environ[\"AZDATA_PASSWORD\"] = mssql_password\n",
|
||||
"if mssql_auth_mode == \"ad\":\n",
|
||||
" os.environ[\"DOMAIN_SERVICE_ACCOUNT_USERNAME\"] = mssql_domain_service_account_username\n",
|
||||
" os.environ[\"DOMAIN_SERVICE_ACCOUNT_PASSWORD\"] = mssql_domain_service_account_password\n",
|
||||
"if os.name == 'nt':\n",
|
||||
" print(f'If you don\\'t see output produced by azdata, you can run the following command in a terminal window to check the deployment status:\\n\\tkubectl get pods -n {mssql_cluster_name} ')\n",
|
||||
"run_command(f'azdata bdc create -c {mssql_target_profile}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "0a743e88-e7d0-4b41-b8a3-e43985d15f2b",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 7
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -57,7 +57,7 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Check dependencies**"
|
||||
"### **Setup**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "a56d3413-a730-4997-b5c2-c8abd972757e"
|
||||
@@ -78,11 +78,7 @@
|
||||
" !{command}\n",
|
||||
" if _exit_code != 0:\n",
|
||||
" sys.exit(f'Command execution failed with exit code: {str(_exit_code)}.\\n\\t{command}\\n')\n",
|
||||
" print(f'Successfully executed: {command}')\n",
|
||||
"\n",
|
||||
"run_command('kubectl version --client=true')\n",
|
||||
"run_command('azdata --version')\n",
|
||||
"run_command('az --version')"
|
||||
" print(f'Successfully executed: {command}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "326645cf-022a-47f2-8aff-37de71da8955",
|
||||
@@ -103,6 +99,31 @@
|
||||
"azdata_cell_guid": "8716915b-1439-431b-ab0a-0221ef94cb7f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Check dependencies**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "db8b1e21-eb2c-4c35-b973-bc4ef38bb1d0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"run_command('kubectl version --client=true')\n",
|
||||
"run_command('azdata --version')\n",
|
||||
"run_command('az --version')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "9361deaf-28b1-4d02-912d-2011cae97e8a",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 0
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Check dependencies**"
|
||||
"### **Setup**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "e3dd8e75-e15f-44b4-81fc-1f54d6f0b1e2"
|
||||
@@ -77,10 +77,7 @@
|
||||
" !{command}\n",
|
||||
" if _exit_code != 0:\n",
|
||||
" sys.exit(f'Command execution failed with exit code: {str(_exit_code)}.\\n\\t{command}\\n')\n",
|
||||
" print(f'Successfully executed: {command}')\n",
|
||||
"\n",
|
||||
"run_command('kubectl version --client=true')\n",
|
||||
"run_command('azdata --version')"
|
||||
" print(f'Successfully executed: {command}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "d973d5b4-7f0a-4a9d-b204-a16480f3940d",
|
||||
@@ -101,6 +98,30 @@
|
||||
"azdata_cell_guid": "4b266b2d-bd1b-4565-92c9-3fc146cdce6d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Check dependencies**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "2544648b-59c9-4ce5-a3b6-87086e214d4c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"run_command('kubectl version --client=true')\n",
|
||||
"run_command('azdata --version')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "691671d7-3f05-406c-a183-4cff7d17f83d",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 0
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Check dependencies**"
|
||||
"### **Setup**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "a31f9894-903f-4e19-a5a8-6fd888ff013b"
|
||||
@@ -77,16 +77,13 @@
|
||||
" !{command}\n",
|
||||
" if _exit_code != 0:\n",
|
||||
" sys.exit(f'Command execution failed with exit code: {str(_exit_code)}.\\n\\t{command}\\n')\n",
|
||||
" print(f'Successfully executed: {command}')\n",
|
||||
"\n",
|
||||
"run_command('kubectl version --client=true')\n",
|
||||
"run_command('azdata --version')"
|
||||
" print(f'Successfully executed: {command}')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "26fa8bc4-4b8e-4c31-ae11-50484821cea8",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 1
|
||||
@@ -101,6 +98,30 @@
|
||||
"azdata_cell_guid": "e70640d0-6059-4cab-939e-e985a978c0da"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
"### **Check dependencies**"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "869d0397-a280-4dc4-be76-d652189b5131"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"run_command('kubectl version --client=true')\n",
|
||||
"run_command('azdata --version')"
|
||||
],
|
||||
"metadata": {
|
||||
"azdata_cell_guid": "c38afb67-1132-495e-9af1-35bf067acbeb",
|
||||
"tags": [
|
||||
"hide_input"
|
||||
]
|
||||
},
|
||||
"outputs": [],
|
||||
"execution_count": 0
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"source": [
|
||||
@@ -310,4 +331,4 @@
|
||||
"execution_count": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,23 @@
|
||||
"microsoft.notebook"
|
||||
],
|
||||
"contributes": {
|
||||
"configuration": [
|
||||
{
|
||||
"title": "%deployment.configuration.title%",
|
||||
"properties": {
|
||||
"deployment.azdataPipInstallUri": {
|
||||
"type": "string",
|
||||
"default": "https://aka.ms/azdata",
|
||||
"description": "%azdata-pip-install-uri-description%"
|
||||
},
|
||||
"deployment.azdataPipInstallArgs": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "%azdata-pip-install-args-description%"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"commands": [
|
||||
{
|
||||
"command": "azdata.resource.deploy",
|
||||
@@ -227,8 +244,7 @@
|
||||
{
|
||||
"wizard": {
|
||||
"type": "new-aks",
|
||||
"notebook": "%bdc-2019-aks-notebook%",
|
||||
"azdata_notebook": "%azdata-bdc-2019-aks-notebook%"
|
||||
"notebook": "%bdc-2019-aks-notebook%"
|
||||
},
|
||||
"requiredTools": [
|
||||
{
|
||||
@@ -238,7 +254,8 @@
|
||||
"name": "azure-cli"
|
||||
},
|
||||
{
|
||||
"name": "azdata"
|
||||
"name": "azdata",
|
||||
"version": "15.0.2070"
|
||||
}
|
||||
],
|
||||
"when": "target=new-aks&&version=bdc2019"
|
||||
@@ -246,8 +263,7 @@
|
||||
{
|
||||
"wizard": {
|
||||
"type": "existing-aks",
|
||||
"notebook": "%bdc-2019-existing-aks-notebook%",
|
||||
"azdata_notebook": "%azdata-bdc-2019-existing-aks-notebook"
|
||||
"notebook": "%bdc-2019-existing-aks-notebook%"
|
||||
},
|
||||
"requiredTools": [
|
||||
{
|
||||
@@ -262,8 +278,7 @@
|
||||
{
|
||||
"wizard": {
|
||||
"type": "existing-kubeadm",
|
||||
"notebook": "%bdc-2019-existing-kubeadm-notebook%",
|
||||
"azdata_notebook": "%azdata-bdc-2019-existing-kubeadm-notebook%"
|
||||
"notebook": "%bdc-2019-existing-kubeadm-notebook%"
|
||||
},
|
||||
"requiredTools": [
|
||||
{
|
||||
@@ -340,7 +355,6 @@
|
||||
"dependencies": {
|
||||
"promisify-child-process": "^3.1.1",
|
||||
"sudo-prompt": "^9.0.0",
|
||||
"vscode": "^1.1.26",
|
||||
"vscode-nls": "^4.0.0",
|
||||
"yamljs": "^0.3.0"
|
||||
},
|
||||
@@ -348,6 +362,7 @@
|
||||
"@types/yamljs": "0.2.30",
|
||||
"mocha-junit-reporter": "^1.17.0",
|
||||
"mocha-multi-reporters": "^1.1.7",
|
||||
"typemoq": "^2.1.0"
|
||||
"typemoq": "^2.1.0",
|
||||
"vscode": "^1.1.26"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
"resource-type-sql-bdc-description": "SQL Server Big Data Cluster allows you to deploy scalable clusters of SQL Server, Spark, and HDFS containers running on Kubernetes",
|
||||
"version-display-name": "Version",
|
||||
"sql-2017-display-name": "SQL Server 2017",
|
||||
"sql-2019-display-name": "SQL Server 2019 RC",
|
||||
"sql-2019-display-name": "SQL Server 2019",
|
||||
"sql-2017-docker-notebook": "./notebooks/docker/2017/deploy-sql2017-image.ipynb",
|
||||
"sql-2019-docker-notebook": "./notebooks/docker/2019/deploy-sql2019-image.ipynb",
|
||||
"bdc-2019-display-name": "SQL Server 2019 RC",
|
||||
"bdc-2019-display-name": "SQL Server 2019",
|
||||
"bdc-deployment-target": "Deployment target",
|
||||
"bdc-deployment-target-new-aks": "New Azure Kubernetes Service Cluster",
|
||||
"bdc-deployment-target-existing-aks": "Existing Azure Kubernetes Service Cluster",
|
||||
@@ -20,9 +20,6 @@
|
||||
"bdc-2019-aks-notebook": "./notebooks/bdc/2019/deploy-bdc-aks.ipynb",
|
||||
"bdc-2019-existing-aks-notebook": "./notebooks/bdc/2019/deploy-bdc-existing-aks.ipynb",
|
||||
"bdc-2019-existing-kubeadm-notebook": "./notebooks/bdc/2019/deploy-bdc-existing-kubeadm.ipynb",
|
||||
"azdata-bdc-2019-aks-notebook": "./notebooks/bdc/2019/azdata/deploy-bdc-aks.ipynb",
|
||||
"azdata-bdc-2019-existing-aks-notebook": "./notebooks/bdc/2019/azdata/deploy-bdc-existing-aks.ipynb",
|
||||
"azdata-bdc-2019-existing-kubeadm-notebook": "./notebooks/bdc/2019/azdata/deploy-bdc-existing-kubeadm.ipynb",
|
||||
"docker-sql-2017-title": "Deploy SQL Server 2017 container images with docker",
|
||||
"docker-sql-2019-title": "Deploy SQL Server 2019 container images with docker",
|
||||
"docker-container-name-field": "Container name",
|
||||
@@ -51,7 +48,10 @@
|
||||
"resource-type-sql-windows-setup-display-name": "SQL Server on Windows",
|
||||
"resource-type-sql-windows-setup-description": "Run SQL Server on Windows, select a version to get started.",
|
||||
"bdc-agreement": "I accept {0}, {1} and {2}.",
|
||||
"bdc-agreement-privacy-statement":"Microsoft Privacy Statement",
|
||||
"bdc-agreement-azdata-eula":"azdata License Terms",
|
||||
"bdc-agreement-bdc-eula":"SQL Server License Terms"
|
||||
"bdc-agreement-privacy-statement": "Microsoft Privacy Statement",
|
||||
"bdc-agreement-azdata-eula": "azdata License Terms",
|
||||
"bdc-agreement-bdc-eula": "SQL Server License Terms",
|
||||
"deployment.configuration.title": "Deployment configuration",
|
||||
"azdata-pip-install-uri-description": "Location of the azdata package used for the pip install command",
|
||||
"azdata-pip-install-args-description": "Additional arguments for the pip install azdata command"
|
||||
}
|
||||
|
||||
8
extensions/resource-deployment/src/constants.ts
Normal file
8
extensions/resource-deployment/src/constants.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export const DeploymentConfigurationKey: string = 'deployment';
|
||||
export const AzdataPipInstallUriKey: string = 'azdataPipInstallUri';
|
||||
export const azdataPipInstallArgsKey: string = 'azdataPipInstallArgs';
|
||||
@@ -93,12 +93,13 @@ export type DeploymentProvider = DialogDeploymentProvider | WizardDeploymentProv
|
||||
|
||||
export interface WizardInfo {
|
||||
notebook: string | NotebookInfo;
|
||||
azdata_notebook: string | NotebookInfo;
|
||||
type: BdcDeploymentType;
|
||||
}
|
||||
|
||||
export interface NotebookBasedDialogInfo extends DialogInfoBase {
|
||||
notebook: string | NotebookInfo;
|
||||
runNotebook?: boolean;
|
||||
taskName?: string;
|
||||
}
|
||||
|
||||
export interface CommandBasedDialogInfo extends DialogInfoBase {
|
||||
@@ -119,6 +120,7 @@ export interface DialogInfoBase {
|
||||
title: string;
|
||||
name: string;
|
||||
tabs: DialogTabInfo[];
|
||||
actionText?: string;
|
||||
}
|
||||
|
||||
export interface DialogTabInfo {
|
||||
@@ -227,17 +229,20 @@ export const enum ToolStatus {
|
||||
}
|
||||
|
||||
export interface ITool {
|
||||
isInstalling: any;
|
||||
readonly isInstalling: boolean;
|
||||
readonly name: string;
|
||||
readonly displayName: string;
|
||||
readonly description: string;
|
||||
readonly type: ToolType;
|
||||
readonly homePage: string;
|
||||
readonly displayStatus: string;
|
||||
readonly dependencyMessages: string[];
|
||||
readonly statusDescription: string | undefined;
|
||||
readonly autoInstallSupported: boolean;
|
||||
readonly autoInstallRequired: boolean;
|
||||
readonly isNotInstalled: boolean;
|
||||
readonly isInstalled: boolean;
|
||||
readonly installationPath: string;
|
||||
readonly needsInstallation: boolean;
|
||||
readonly outputChannelName: string;
|
||||
readonly fullVersion: string | undefined;
|
||||
@@ -245,6 +250,7 @@ export interface ITool {
|
||||
showOutputChannel(preserveFocus?: boolean): void;
|
||||
loadInformation(): Promise<void>;
|
||||
install(): Promise<void>;
|
||||
isSameOrNewerThan(version: string): boolean;
|
||||
}
|
||||
|
||||
export const enum BdcDeploymentType {
|
||||
|
||||
@@ -52,7 +52,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
}
|
||||
});
|
||||
vscode.commands.registerCommand('azdata.openNotebookInputDialog', (dialogInfo: NotebookBasedDialogInfo) => {
|
||||
const dialog = new DeploymentInputDialog(notebookService, dialogInfo);
|
||||
const dialog = new DeploymentInputDialog(notebookService, platformService, dialogInfo);
|
||||
dialog.open();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export interface INotebookService {
|
||||
launchNotebook(notebook: string | NotebookInfo): Thenable<azdata.nb.NotebookEditor>;
|
||||
launchNotebookWithContent(title: string, content: string): Thenable<azdata.nb.NotebookEditor>;
|
||||
getNotebook(notebook: string | NotebookInfo): Promise<Notebook>;
|
||||
executeNotebook(notebook: any, env: NodeJS.ProcessEnv): Promise<NotebookExecutionResult>;
|
||||
executeNotebook(notebook: any, env?: NodeJS.ProcessEnv): Promise<NotebookExecutionResult>;
|
||||
}
|
||||
|
||||
export class NotebookService implements INotebookService {
|
||||
@@ -73,17 +73,21 @@ export class NotebookService implements INotebookService {
|
||||
return <Notebook>JSON.parse(await this.platformService.readTextFile(notebookPath));
|
||||
}
|
||||
|
||||
async executeNotebook(notebook: Notebook, env: NodeJS.ProcessEnv): Promise<NotebookExecutionResult> {
|
||||
async executeNotebook(notebook: Notebook, env?: NodeJS.ProcessEnv): Promise<NotebookExecutionResult> {
|
||||
const content = JSON.stringify(notebook, undefined, 4);
|
||||
const fileName = `nb-${getDateTimeString()}.ipynb`;
|
||||
const workingDirectory = this.platformService.storagePath();
|
||||
const notebookFullPath = path.join(workingDirectory, fileName);
|
||||
const outputFullPath = path.join(workingDirectory, `output-${fileName}`);
|
||||
const additionalEnvironmentVariables: NodeJS.ProcessEnv = env || {};
|
||||
// Set the azdata eula
|
||||
// Scenarios using the executeNotebook feature already have the EULA acceptted by the user before executing this.
|
||||
additionalEnvironmentVariables['ACCEPT_EULA'] = 'yes';
|
||||
try {
|
||||
await this.platformService.saveTextFile(content, notebookFullPath);
|
||||
await this.platformService.runCommand(`azdata notebook run --path "${notebookFullPath}" --output-path "${workingDirectory}" --timeout -1`,
|
||||
{
|
||||
additionalEnvironmentVariables: env,
|
||||
additionalEnvironmentVariables: additionalEnvironmentVariables,
|
||||
workingDirectory: workingDirectory
|
||||
});
|
||||
return {
|
||||
|
||||
@@ -157,7 +157,7 @@ export class PlatformService implements IPlatformService {
|
||||
return await this.runStreamedCommand(command, this._outputChannel, options);
|
||||
}
|
||||
} catch (error) {
|
||||
this._outputChannel.append(localize('platformService.RunCommand.ErroredOut', "\t>>> {0} ... errored out: {1}", command, getErrorMessage(error))); //errors are localized in our code where emitted, other errors are pass through from external components that are not easily localized
|
||||
this._outputChannel.append(localize('platformService.RunCommand.ErroredOut', "\t>>> {0} … errored out: {1}", command, getErrorMessage(error))); //errors are localized in our code where emitted, other errors are pass through from external components that are not easily localized
|
||||
if (!(options && options.ignoreError)) {
|
||||
throw error;
|
||||
} else {
|
||||
@@ -232,9 +232,9 @@ export class PlatformService implements IPlatformService {
|
||||
// Add listeners to print stdout and stderr and exit code
|
||||
child.on('exit', (code: number | null, signal: string | null) => {
|
||||
if (code !== null) {
|
||||
outputChannel.appendLine(localize('platformService.RunStreamedCommand.ExitedWithCode', " >>> ${0} ... exited with code: ${1}", command, code));
|
||||
outputChannel.appendLine(localize('platformService.RunStreamedCommand.ExitedWithCode', " >>> {0} … exited with code: {1}", command, code));
|
||||
} else {
|
||||
outputChannel.appendLine(localize('platformService.RunStreamedCommand.ExitedWithSignal', " >>> ${0} ... exited with signal: ${1}", command, signal));
|
||||
outputChannel.appendLine(localize('platformService.RunStreamedCommand.ExitedWithSignal', " >>> {0} … exited with signal: {1}", command, signal));
|
||||
}
|
||||
});
|
||||
child.stdout.on('data', (data: string | Buffer) => {
|
||||
|
||||
@@ -234,7 +234,7 @@ export class ResourceTypeService implements IResourceTypeService {
|
||||
const wizard = new DeployClusterWizard(provider.wizard, new KubeService(), new AzdataService(this.platformService), this.notebookService);
|
||||
wizard.open();
|
||||
} else if (instanceOfDialogDeploymentProvider(provider)) {
|
||||
const dialog = new DeploymentInputDialog(this.notebookService, provider.dialog);
|
||||
const dialog = new DeploymentInputDialog(this.notebookService, this.platformService, provider.dialog);
|
||||
dialog.open();
|
||||
} else if (instanceOfNotebookDeploymentProvider(provider)) {
|
||||
this.notebookService.launchNotebook(provider.notebook);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { SemVer } from 'semver';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { Command, OsType, ToolType } from '../../interfaces';
|
||||
import { IPlatformService } from '../platformService';
|
||||
import { ToolBase } from './toolBase';
|
||||
import { ToolBase, dependencyType } from './toolBase';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
const defaultInstallationRoot = '~/.local/bin';
|
||||
@@ -42,16 +42,16 @@ export class AzCliTool extends ToolBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected async getInstallationPath(): Promise<string | undefined> {
|
||||
protected async getSearchPaths(): Promise<string[]> {
|
||||
switch (this.osType) {
|
||||
case OsType.win32:
|
||||
return win32InstallationRoot;
|
||||
return [win32InstallationRoot];
|
||||
default:
|
||||
return defaultInstallationRoot;
|
||||
return [defaultInstallationRoot];
|
||||
}
|
||||
}
|
||||
|
||||
readonly allInstallationCommands: Map<OsType, Command[]> = new Map<OsType, Command[]>([
|
||||
protected readonly allInstallationCommands: Map<OsType, Command[]> = new Map<OsType, Command[]>([
|
||||
[OsType.linux, linuxInstallationCommands],
|
||||
[OsType.win32, win32InstallationCommands],
|
||||
[OsType.darwin, macOsInstallationCommands],
|
||||
@@ -65,75 +65,90 @@ export class AzCliTool extends ToolBase {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
protected get versionCommand(): Command {
|
||||
return {
|
||||
command: 'az --version'
|
||||
};
|
||||
}
|
||||
|
||||
protected dependenciesByOsType: Map<OsType, dependencyType[]> = new Map<OsType, dependencyType[]>([
|
||||
[OsType.linux, []],
|
||||
[OsType.win32, []],
|
||||
[OsType.darwin, [dependencyType.Brew]],
|
||||
[OsType.others, [dependencyType.Curl]]
|
||||
]);
|
||||
|
||||
protected get discoveryCommand(): Command {
|
||||
return {
|
||||
command: this.discoveryCommandString('az')
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const win32InstallationCommands = [
|
||||
{
|
||||
comment: localize('resourceDeployment.AziCli.DeletingPreviousAzureCli.msi', "deleting previously downloaded azurecli.msi if one exists ..."),
|
||||
comment: localize('resourceDeployment.AziCli.DeletingPreviousAzureCli.msi', "deleting previously downloaded azurecli.msi if one exists …"),
|
||||
command: `IF EXIST .\\AzureCLI.msi DEL /F .\\AzureCLI.msi`
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.AziCli.DownloadingAndInstallingAzureCli', "downloading azurecli.msi and installing azure-cli ..."),
|
||||
comment: localize('resourceDeployment.AziCli.DownloadingAndInstallingAzureCli', "downloading azurecli.msi and installing azure-cli …"),
|
||||
command: `powershell -Command "& {(New-Object System.Net.WebClient).DownloadFile('https://aka.ms/installazurecliwindows', 'AzureCLI.msi'); Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /passive /quiet /lvx ADS_AzureCliInstall.log'}"`
|
||||
},
|
||||
{
|
||||
comment: localize('resourceDeployment.AziCli.DisplayingInstallationLog', "displaying the installation log ..."),
|
||||
comment: localize('resourceDeployment.AziCli.DisplayingInstallationLog', "displaying the installation log …"),
|
||||
command: `type AzureCliInstall.log | findstr /i /v /c:"cached product context" | findstr /i /v /c:"has no eligible binary patches" `,
|
||||
ignoreError: true
|
||||
}
|
||||
];
|
||||
const macOsInstallationCommands = [
|
||||
// try to install brew ourselves
|
||||
{
|
||||
comment: localize('resourceDeployment.AziCli.UpdatingBrewRepository', "updating your brew repository for azure-cli installation ..."),
|
||||
comment: localize('resourceDeployment.AziCli.UpdatingBrewRepository', "updating your brew repository for azure-cli installation …"),
|
||||
command: 'brew update'
|
||||
},
|
||||
{
|
||||
comment: localize('resourceDeployment.AziCli.InstallingAzureCli', "installing azure-cli ..."),
|
||||
comment: localize('resourceDeployment.AziCli.InstallingAzureCli', "installing azure-cli …"),
|
||||
command: 'brew install azure-cli'
|
||||
}
|
||||
];
|
||||
const linuxInstallationCommands = [
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.AziCli.AptGetUpdate', "updating repository information before installing azure-cli ..."),
|
||||
comment: localize('resourceDeployment.AziCli.AptGetUpdate', "updating repository information before installing azure-cli …"),
|
||||
command: 'apt-get update'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.AziCli.AptGetPackages', "getting packages needed for azure-cli installation ..."),
|
||||
comment: localize('resourceDeployment.AziCli.AptGetPackages', "getting packages needed for azure-cli installation …"),
|
||||
command: 'apt-get install ca-certificates curl apt-transport-https lsb-release gnupg -y'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.AziCli.DownloadAndInstallingSigningKey', "downloading and installing the signing key for azure-cli ..."),
|
||||
comment: localize('resourceDeployment.AziCli.DownloadAndInstallingSigningKey', "downloading and installing the signing key for azure-cli …"),
|
||||
command: 'curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | tee /etc/apt/trusted.gpg.d/microsoft.asc.gpg > /dev/null'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.AziCli.AddingAzureCliRepositoryInformation', "adding the azure-cli repository information ..."),
|
||||
comment: localize('resourceDeployment.AziCli.AddingAzureCliRepositoryInformation', "adding the azure-cli repository information …"),
|
||||
command: 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ `lsb_release -cs` main" | tee /etc/apt/sources.list.d/azure-cli.list'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.AziCli.AptGetUpdateAgain', "updating repository information again for azure-cli ..."),
|
||||
comment: localize('resourceDeployment.AziCli.AptGetUpdateAgain', "updating repository information again for azure-cli …"),
|
||||
command: 'apt-get update'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.AziCli.InstallingAzureCli', "installing azure-cli ..."),
|
||||
comment: localize('resourceDeployment.AziCli.InstallingAzureCli', "installing azure-cli …"),
|
||||
command: 'apt-get install azure-cli'
|
||||
}
|
||||
];
|
||||
const defaultInstallationCommands = [
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.AziCli.ScriptedInstall', "download and invoking script to install azure-cli ..."),
|
||||
comment: localize('resourceDeployment.AziCli.ScriptedInstall', "download and invoking script to install azure-cli …"),
|
||||
command: 'curl -sL https://aka.ms/InstallAzureCLIDeb | bash'
|
||||
}
|
||||
];
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
import { EOL } from 'os';
|
||||
import * as path from 'path';
|
||||
import { SemVer } from 'semver';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { azdataPipInstallArgsKey, AzdataPipInstallUriKey, DeploymentConfigurationKey } from '../../constants';
|
||||
import { Command, OsType, ToolType } from '../../interfaces';
|
||||
import { IPlatformService } from '../platformService';
|
||||
import { ToolBase } from './toolBase';
|
||||
import { dependencyType, ToolBase } from './toolBase';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
const installationRoot = '~/.local/bin';
|
||||
|
||||
export class AzdataTool extends ToolBase {
|
||||
constructor(platformService: IPlatformService) {
|
||||
@@ -44,6 +45,12 @@ export class AzdataTool extends ToolBase {
|
||||
};
|
||||
}
|
||||
|
||||
protected get discoveryCommand(): Command {
|
||||
return {
|
||||
command: this.discoveryCommandString('azdata')
|
||||
};
|
||||
}
|
||||
|
||||
protected getVersionFromOutput(output: string): SemVer | undefined {
|
||||
let version: SemVer | undefined = undefined;
|
||||
if (output && output.split(EOL).length > 0) {
|
||||
@@ -56,74 +63,95 @@ export class AzdataTool extends ToolBase {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected async getInstallationPath(): Promise<string | undefined> {
|
||||
protected async getSearchPaths(): Promise<string[]> {
|
||||
switch (this.osType) {
|
||||
case OsType.linux:
|
||||
return installationRoot;
|
||||
default:
|
||||
const azdataCliInstallLocation = await this.getPip3InstallLocation('azdata-cli');
|
||||
return azdataCliInstallLocation && path.join(azdataCliInstallLocation, '..', 'Scripts');
|
||||
if (azdataCliInstallLocation) {
|
||||
return [path.join(azdataCliInstallLocation, '..', 'Scripts'), path.join(azdataCliInstallLocation, '..', '..', '..', 'bin')];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
readonly allInstallationCommands: Map<OsType, Command[]> = new Map<OsType, Command[]>([
|
||||
[OsType.linux, linuxInstallationCommands],
|
||||
[OsType.win32, defaultInstallationCommands],
|
||||
[OsType.darwin, defaultInstallationCommands],
|
||||
[OsType.others, defaultInstallationCommands]
|
||||
]);
|
||||
protected get allInstallationCommands(): Map<OsType, Command[]> {
|
||||
return new Map<OsType, Command[]>([
|
||||
[OsType.linux, this.defaultInstallationCommands],
|
||||
[OsType.win32, this.defaultInstallationCommands],
|
||||
[OsType.darwin, this.defaultInstallationCommands],
|
||||
[OsType.others, this.defaultInstallationCommands]
|
||||
]);
|
||||
}
|
||||
|
||||
protected get uninstallCommand(): string | undefined {
|
||||
if (this.osType !== OsType.linux) {
|
||||
return defaultUninstallCommand;
|
||||
} else {
|
||||
return super.uninstallCommand;
|
||||
}
|
||||
return this.defaultUninstallCommand;
|
||||
}
|
||||
|
||||
private get defaultInstallationCommands(): Command[] {
|
||||
return [
|
||||
{
|
||||
comment: localize('resourceDeployment.Azdata.InstallUpdatePythonRequestsPackage', "installing/updating to latest version of requests python package azdata …"),
|
||||
command: `pip3 install -U requests`
|
||||
},
|
||||
{
|
||||
comment: localize('resourceDeployment.Azdata.InstallingAzdata', "installing azdata …"),
|
||||
command: `pip3 install -r ${this.azdataInstallUri} ${this.azdataInstallAdditionalArgs} --quiet --user`
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
private get defaultUninstallCommand(): string {
|
||||
return `pip3 uninstall -r ${this.azdataInstallUri} ${this.azdataInstallAdditionalArgs} -y `;
|
||||
}
|
||||
|
||||
private get azdataInstallUri(): string {
|
||||
return vscode.workspace.getConfiguration(DeploymentConfigurationKey)[AzdataPipInstallUriKey];
|
||||
}
|
||||
|
||||
private get azdataInstallAdditionalArgs(): string {
|
||||
return vscode.workspace.getConfiguration(DeploymentConfigurationKey)[azdataPipInstallArgsKey];
|
||||
}
|
||||
|
||||
protected dependenciesByOsType: Map<OsType, dependencyType[]> = new Map<OsType, dependencyType[]>([
|
||||
[OsType.linux, [dependencyType.PythonAndPip3]],
|
||||
[OsType.win32, [dependencyType.PythonAndPip3]],
|
||||
[OsType.darwin, [dependencyType.PythonAndPip3]],
|
||||
[OsType.others, [dependencyType.PythonAndPip3]]
|
||||
]);
|
||||
}
|
||||
|
||||
/*
|
||||
const linuxInstallationCommands = [
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Azdata.AptGetUpdate', "updating repository information ..."),
|
||||
comment: localize('resourceDeployment.Azdata.AptGetUpdate', "updating repository information …"),
|
||||
command: 'apt-get update'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Azdata.AptGetPackages', "getting packages needed for azdata installation ..."),
|
||||
comment: localize('resourceDeployment.Azdata.AptGetPackages', "getting packages needed for azdata installation …"),
|
||||
command: 'apt-get install gnupg ca-certificates curl apt-transport-https lsb-release -y'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Azdata.DownloadAndInstallingSigningKey', "downloading and installing the signing key for azdata ..."),
|
||||
comment: localize('resourceDeployment.Azdata.DownloadAndInstallingSigningKey', "downloading and installing the signing key for azdata …"),
|
||||
command: 'wget -qO- https://packages.microsoft.com/keys/microsoft.asc | apt-key add -'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Azdata.AddingAzureCliRepositoryInformation', "adding the azdata repository information ..."),
|
||||
comment: localize('resourceDeployment.Azdata.AddingAzureCliRepositoryInformation', "adding the azdata repository information …"),
|
||||
command: 'add-apt-repository "$(wget -qO- https://packages.microsoft.com/config/ubuntu/16.04/mssql-server-preview.list)"'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Azdata.AptGetUpdate', "updating repository information ..."),
|
||||
comment: localize('resourceDeployment.Azdata.AptGetUpdate', "updating repository information …"),
|
||||
command: 'apt-get update'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Azdata.InstallingAzdata', "installing azdata ..."),
|
||||
comment: localize('resourceDeployment.Azdata.InstallingAzdata', "installing azdata …"),
|
||||
command: 'apt-get install -y azdata-cli'
|
||||
}
|
||||
];
|
||||
|
||||
const defaultInstallationCommands = [
|
||||
{
|
||||
comment: localize('resourceDeployment.Azdata.InstallUpdatePythonRequestsPackage', "installing/updating to latest version of requests python package azdata ..."),
|
||||
command: `pip3 install -U requests`
|
||||
},
|
||||
{
|
||||
comment: localize('resourceDeployment.Azdata.InstallingAzdata', "installing azdata ..."),
|
||||
command: `pip3 install -r https://aka.ms/azdata --quiet --user`
|
||||
}
|
||||
];
|
||||
|
||||
const defaultUninstallCommand = `pip3 uninstall -r https://aka.ms/azdata -y `;
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@ import { ToolBase } from './toolBase';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class DockerTool extends ToolBase {
|
||||
protected discoveryCommand: Command = { command: '' };
|
||||
constructor(platformService: IPlatformService) {
|
||||
super(platformService);
|
||||
}
|
||||
@@ -50,7 +51,7 @@ export class DockerTool extends ToolBase {
|
||||
return false;
|
||||
}
|
||||
|
||||
get allInstallationCommands(): Map<OsType, Command[]> {
|
||||
protected get allInstallationCommands(): Map<OsType, Command[]> {
|
||||
throw Error('Installation of DockerTool is not supported');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,12 @@ import { Command, ToolType, OsType } from '../../interfaces';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { SemVer } from 'semver';
|
||||
import { IPlatformService } from '../platformService';
|
||||
import { ToolBase } from './toolBase';
|
||||
import { ToolBase, dependencyType } from './toolBase';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
const defaultInstallationRoot = '/usr/local/bin';
|
||||
|
||||
export class KubeCtlTool extends ToolBase {
|
||||
constructor(platformService: IPlatformService) {
|
||||
super(platformService);
|
||||
@@ -49,96 +51,116 @@ export class KubeCtlTool extends ToolBase {
|
||||
return { command: 'kubectl version -o json --client' };
|
||||
}
|
||||
|
||||
protected get discoveryCommand(): Command {
|
||||
return {
|
||||
command: this.discoveryCommandString('kubectl')
|
||||
};
|
||||
}
|
||||
|
||||
get autoInstallSupported(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
readonly allInstallationCommands: Map<OsType, Command[]> = new Map<OsType, Command[]>([
|
||||
protected async getSearchPaths(): Promise<string[]> {
|
||||
switch (this.osType) {
|
||||
case OsType.win32:
|
||||
return [this.storagePath];
|
||||
default:
|
||||
return [defaultInstallationRoot];
|
||||
}
|
||||
}
|
||||
protected readonly allInstallationCommands: Map<OsType, Command[]> = new Map<OsType, Command[]>([
|
||||
[OsType.linux, linuxInstallationCommands],
|
||||
[OsType.win32, win32InstallationCommands],
|
||||
[OsType.darwin, macOsInstallationCommands],
|
||||
[OsType.others, defaultInstallationCommands]
|
||||
]);
|
||||
|
||||
protected dependenciesByOsType: Map<OsType, dependencyType[]> = new Map<OsType, dependencyType[]>([
|
||||
[OsType.linux, []],
|
||||
[OsType.win32, []],
|
||||
[OsType.darwin, [dependencyType.Brew]],
|
||||
[OsType.others, [dependencyType.Curl]]
|
||||
]);
|
||||
}
|
||||
|
||||
const macOsInstallationCommands = [
|
||||
{
|
||||
comment: localize('resourceDeployment.Kubectl.UpdatingBrewRepository', "updating your brew repository for kubectl installation ..."),
|
||||
comment: localize('resourceDeployment.Kubectl.UpdatingBrewRepository', "updating your brew repository for kubectl installation …"),
|
||||
command: 'brew update'
|
||||
},
|
||||
{
|
||||
comment: localize('resourceDeployment.Kubectl.InstallingKubeCtl', "installing kubectl ..."),
|
||||
comment: localize('resourceDeployment.Kubectl.InstallingKubeCtl', "installing kubectl …"),
|
||||
command: 'brew install kubectl'
|
||||
}
|
||||
];
|
||||
const linuxInstallationCommands = [
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Kubectl.AptGetUpdate', "updating repository information ..."),
|
||||
comment: localize('resourceDeployment.Kubectl.AptGetUpdate', "updating repository information …"),
|
||||
command: 'apt-get update'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Kubectl.AptGetPackages', "getting packages needed for kubectl installation ..."),
|
||||
comment: localize('resourceDeployment.Kubectl.AptGetPackages', "getting packages needed for kubectl installation …"),
|
||||
command: 'apt-get install -y apt-transport-https'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Kubectl.DownloadAndInstallingSigningKey', "downloading and installing the signing key for kubectl ..."),
|
||||
comment: localize('resourceDeployment.Kubectl.DownloadAndInstallingSigningKey', "downloading and installing the signing key for kubectl …"),
|
||||
command: 'curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Kubectl.AddingKubectlRepositoryInformation', "adding the kubectl repository information ..."),
|
||||
comment: localize('resourceDeployment.Kubectl.AddingKubectlRepositoryInformation', "adding the kubectl repository information …"),
|
||||
command: 'echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | tee -a /etc/apt/sources.list.d/kubernetes.list'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Kubectl.AptGetUpdate', "updating repository information ..."),
|
||||
comment: localize('resourceDeployment.Kubectl.AptGetUpdate', "updating repository information …"),
|
||||
command: 'apt-get update'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Kubectl.InstallingKubectl', "installing kubectl ..."),
|
||||
comment: localize('resourceDeployment.Kubectl.InstallingKubectl', "installing kubectl …"),
|
||||
command: 'apt-get install -y kubectl'
|
||||
}
|
||||
];
|
||||
// TODO: Remove dependency on curl on Win32 and use powershell Invoke-WebRequest instead
|
||||
const win32InstallationCommands = [
|
||||
{
|
||||
comment: localize('resourceDeployment.Kubectl.DeletePreviousDownloadedKubectl.exe', "deleting previously downloaded kubectl.exe if one exists ..."),
|
||||
command: `IF EXIST .\kubectl.exe DEL /F .\kubectl.exe`,
|
||||
comment: localize('resourceDeployment.Kubectl.DeletePreviousDownloadedKubectl.exe', "deleting previously downloaded kubectl.exe if one exists …"),
|
||||
command: 'IF EXIST .\\kubectl.exe DEL /F .\\kubectl.exe',
|
||||
},
|
||||
{
|
||||
comment: localize('resourceDeployment.Kubectl.DownloadingAndInstallingKubectl', "downloading and installing the latest kubectl.exe ..."),
|
||||
command: `for /f %i in ('curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt') do curl -LO https://storage.googleapis.com/kubernetes-release/release/%i/bin/windows/amd64/kubectl.exe`
|
||||
comment: localize('resourceDeployment.Kubectl.DownloadingAndInstallingKubectl', "downloading and installing the latest kubectl.exe …"),
|
||||
command: `powershell -Command "& {$WebClient = New-Object System.Net.WebClient; $Version=$WebClient.DownloadString('https://storage.googleapis.com/kubernetes-release/release/stable.txt').Trim();Write-Output \\\"KubeCtl Version=$Version\\\";$Url=\\\"https://storage.googleapis.com/kubernetes-release/release/$Version/bin/windows/amd64/kubectl.exe\\\"; Write-Output \\\"Downloading file: $Url\\\"; $WebClient.DownloadFile($Url, 'kubectl.exe')}"`
|
||||
}
|
||||
];
|
||||
const defaultInstallationCommands = [
|
||||
{
|
||||
comment: localize('resourceDeployment.Kubectl.DeletePreviousDownloadedKubectl', "deleting previously downloaded kubectl if one exists ..."),
|
||||
comment: localize('resourceDeployment.Kubectl.DeletePreviousDownloadedKubectl', "deleting previously downloaded kubectl if one exists …"),
|
||||
command: `[ -e ./kubectl ] && rm -f ./kubectl`,
|
||||
},
|
||||
{
|
||||
comment: localize('resourceDeployment.Kubectl.DownloadingKubectl', "downloading the latest kubectl release ..."),
|
||||
comment: localize('resourceDeployment.Kubectl.DownloadingKubectl', "downloading the latest kubectl release …"),
|
||||
command: 'curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl'
|
||||
},
|
||||
{
|
||||
comment: localize('resourceDeployment.Kubectl.MakingExecutable', "making kubectl executable ..."),
|
||||
comment: localize('resourceDeployment.Kubectl.MakingExecutable', "making kubectl executable …"),
|
||||
command: 'chmod +x ./kubectl',
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Kubectl.CleaningUpOldBackups', "cleaning up any previously backed up version in the install location if they exist ..."),
|
||||
command: `[ -e /usr/local/bin/kubectl] && [ -e /usr/local/bin/kubectl_movedByADS ] && rm -f /usr/local/bin/kubectl_movedByADS`
|
||||
comment: localize('resourceDeployment.Kubectl.CleaningUpOldBackups', "cleaning up any previously backed up version in the install location if they exist …"),
|
||||
command: '[ -e /usr/local/bin/kubectl] && [ -e /usr/local/bin/kubectl_movedByADS ] && rm -f /usr/local/bin/kubectl_movedByADS'
|
||||
},
|
||||
{
|
||||
sudo: true,
|
||||
comment: localize('resourceDeployment.Kubectl.BackupCurrentBinary', "backing up any existing kubectl in the install location ..."),
|
||||
command: `[ -e /usr/local/bin/kubectl ] && mv /usr/local/bin/kubectl /usr/local/bin/kubectl_movedByADS`
|
||||
comment: localize('resourceDeployment.Kubectl.BackupCurrentBinary', "backing up any existing kubectl in the install location …"),
|
||||
command: '[ -e /usr/local/bin/kubectl ] && mv /usr/local/bin/kubectl /usr/local/bin/kubectl_movedByADS'
|
||||
},
|
||||
{
|
||||
comment: localize('resourceDeployment.Kubectl.MoveToSystemPath', "moving kubectl into the install location in the PATH ..."),
|
||||
comment: localize('resourceDeployment.Kubectl.MoveToSystemPath', "moving kubectl into the install location in the PATH …"),
|
||||
sudo: true,
|
||||
command: 'mv ./kubectl /usr/local/bin/kubectl'
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { EOL } from 'os';
|
||||
import { delimiter } from 'path';
|
||||
import { SemVer } from 'semver';
|
||||
import { SemVer, compare } from 'semver';
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { Command, ITool, OsType, ToolStatus, ToolType } from '../../interfaces';
|
||||
@@ -14,7 +14,7 @@ import { IPlatformService } from '../platformService';
|
||||
const localize = nls.loadMessageBundle();
|
||||
const toolStatusNotInstalled: string = localize('deploymentDialog.ToolStatus.NotInstalled', "Not Installed");
|
||||
const toolStatusInstalled: string = localize('deploymentDialog.ToolStatus.Installed', "Installed");
|
||||
const toolStatusInstalling: string = localize('deploymentDialog.ToolStatus.NotInstalling', "Installing ...");
|
||||
const toolStatusInstalling: string = localize('deploymentDialog.ToolStatus.Installing', "Installing…");
|
||||
const toolStatusError: string = localize('deploymentDialog.ToolStatus.Error', "Error");
|
||||
const toolStatusFailed: string = localize('deploymentDialog.ToolStatus.Failed', "Failed");
|
||||
|
||||
@@ -26,6 +26,22 @@ const toolStatusLocalized: Map<ToolStatus, string> = new Map<ToolStatus, string>
|
||||
[ToolStatus.Failed, toolStatusFailed]
|
||||
]);
|
||||
|
||||
export const enum dependencyType {
|
||||
PythonAndPip3 = 'PythonAndPip3',
|
||||
Brew = 'Brew',
|
||||
Curl = 'Curl'
|
||||
}
|
||||
|
||||
const pythonAndPip3Localized = localize('deploymentDialog.ToolInformationalMessage.PythonAndPip3', "• azdata installation needs python3 and pip3 to be pre-installed before necessary tools can be deployed");
|
||||
const brewLocalized = localize('deploymentDialog.ToolInformationalMessage.Brew', "• brew is needed for deployment of the tools and needs to be pre-installed before necessary tools can be deployed");
|
||||
const curlLocalized = localize('deploymentDialog.ToolInformationalMessage.Curl', "• curl is needed for installation and needs to be pre-installed before necessary tools can be deployed");
|
||||
|
||||
export const messageByDependencyType: Map<dependencyType, string> = new Map<dependencyType, string>([
|
||||
[dependencyType.PythonAndPip3, pythonAndPip3Localized],
|
||||
[dependencyType.Brew, brewLocalized],
|
||||
[dependencyType.Curl, curlLocalized]
|
||||
]);
|
||||
|
||||
export abstract class ToolBase implements ITool {
|
||||
constructor(private _platformService: IPlatformService) {
|
||||
this._osType = this._platformService.osType();
|
||||
@@ -37,21 +53,27 @@ export abstract class ToolBase implements ITool {
|
||||
abstract type: ToolType;
|
||||
abstract homePage: string;
|
||||
abstract autoInstallSupported: boolean;
|
||||
abstract readonly allInstallationCommands: Map<OsType, Command[]>;
|
||||
protected abstract readonly allInstallationCommands: Map<OsType, Command[]>;
|
||||
protected readonly dependenciesByOsType: Map<OsType, dependencyType[]> = new Map<OsType, dependencyType[]>();
|
||||
|
||||
protected abstract getVersionFromOutput(output: string): SemVer | undefined;
|
||||
protected readonly _onDidUpdateData = new vscode.EventEmitter<ITool>();
|
||||
protected readonly uninstallCommand?: string;
|
||||
|
||||
|
||||
protected abstract readonly versionCommand: Command;
|
||||
|
||||
public get dependencyMessages(): string[] {
|
||||
return (this.dependenciesByOsType.get(this.osType) || []).map((msgType: dependencyType) => messageByDependencyType.get(msgType)!);
|
||||
}
|
||||
|
||||
protected async getInstallationPath(): Promise<string | undefined> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected get installationSearchPaths(): (string | undefined)[] {
|
||||
return [this.storagePath];
|
||||
protected abstract readonly discoveryCommand: Command;
|
||||
|
||||
protected async getSearchPaths(): Promise<string[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
protected get downloadPath(): string {
|
||||
@@ -87,6 +109,10 @@ export abstract class ToolBase implements ITool {
|
||||
return this.status === ToolStatus.NotInstalled;
|
||||
}
|
||||
|
||||
public get isInstalled(): boolean {
|
||||
return this.status === ToolStatus.Installed;
|
||||
}
|
||||
|
||||
public get isInstalling(): boolean {
|
||||
return this.status === ToolStatus.Installing;
|
||||
}
|
||||
@@ -126,6 +152,9 @@ export abstract class ToolBase implements ITool {
|
||||
return this._statusDescription;
|
||||
}
|
||||
|
||||
public get installationPath(): string {
|
||||
return this._installationPath;
|
||||
}
|
||||
protected get installationCommands(): Command[] | undefined {
|
||||
return this.allInstallationCommands.get(this.osType);
|
||||
}
|
||||
@@ -154,6 +183,7 @@ export abstract class ToolBase implements ITool {
|
||||
}
|
||||
|
||||
public async install(): Promise<void> {
|
||||
this._statusDescription = '';
|
||||
try {
|
||||
this.status = ToolStatus.Installing;
|
||||
await this.installCore();
|
||||
@@ -165,6 +195,7 @@ export abstract class ToolBase implements ITool {
|
||||
this.status = ToolStatus.Error;
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Since we just completed installation, the status should be ToolStatus.Installed
|
||||
// but if it is ToolStatus.NotInstalled then it means that installation failed with 0 exit code.
|
||||
if (this.status === ToolStatus.NotInstalled) {
|
||||
@@ -197,28 +228,30 @@ export abstract class ToolBase implements ITool {
|
||||
}
|
||||
|
||||
protected async addInstallationSearchPathsToSystemPath(): Promise<void> {
|
||||
const installationPath = await this.getInstallationPath();
|
||||
const searchPaths = [installationPath, ...this.installationSearchPaths].filter(path => !!path);
|
||||
const searchPaths = [...await this.getSearchPaths(), this.storagePath].filter(path => !!path);
|
||||
this.logToOutputChannel(localize('toolBase.addInstallationSearchPathsToSystemPath.SearchPaths', "Search Paths for tool '{0}': {1}", this.displayName, JSON.stringify(searchPaths, undefined, '\t'))); //this.displayName is localized and searchPaths are OS filesystem paths.
|
||||
searchPaths.forEach(installationSearchPath => {
|
||||
searchPaths.forEach(searchPath => {
|
||||
if (process.env.PATH) {
|
||||
if (!`${delimiter}${process.env.PATH}${delimiter}`.includes(`${delimiter}${installationSearchPath}${delimiter}`)) {
|
||||
process.env.PATH += `${delimiter}${installationSearchPath}`;
|
||||
console.log(`Appending to Path -> ${delimiter}${installationSearchPath}`);
|
||||
if (!`${delimiter}${process.env.PATH}${delimiter}`.includes(`${delimiter}${searchPath}${delimiter}`)) {
|
||||
process.env.PATH += `${delimiter}${searchPath}`;
|
||||
console.log(`Appending to Path -> '${delimiter}${searchPath}'`);
|
||||
}
|
||||
} else {
|
||||
process.env.PATH = installationSearchPath;
|
||||
console.log(`Appending to Path -> '${delimiter}${installationSearchPath}':${delimiter}${installationSearchPath}`);
|
||||
process.env.PATH = searchPath;
|
||||
console.log(`Setting PATH to -> '${searchPath}'`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async loadInformation(): Promise<void> {
|
||||
if (this.status === ToolStatus.NotInstalled) {
|
||||
await this.addInstallationSearchPathsToSystemPath();
|
||||
this.status = await this.updateVersionAndGetStatus();
|
||||
}
|
||||
}
|
||||
|
||||
private async updateVersionAndGetStatus(): Promise<ToolStatus> {
|
||||
this._statusDescription = '';
|
||||
const commandOutput = await this._platformService.runCommand(
|
||||
this.versionCommand.command,
|
||||
{
|
||||
@@ -230,6 +263,10 @@ export abstract class ToolBase implements ITool {
|
||||
);
|
||||
this.version = this.getVersionFromOutput(commandOutput);
|
||||
if (this.version) {
|
||||
if (this.autoInstallSupported) {
|
||||
// discover and set the installationPath
|
||||
await this.setInstallationPath();
|
||||
}
|
||||
return ToolStatus.Installed;
|
||||
}
|
||||
else {
|
||||
@@ -238,10 +275,43 @@ export abstract class ToolBase implements ITool {
|
||||
}
|
||||
}
|
||||
|
||||
protected discoveryCommandString(toolBinary: string) {
|
||||
switch (this.osType) {
|
||||
case OsType.win32:
|
||||
return `where.exe ${toolBinary}`;
|
||||
case OsType.darwin:
|
||||
return `command -v ${toolBinary}`;
|
||||
default:
|
||||
return `which ${toolBinary}`;
|
||||
}
|
||||
}
|
||||
|
||||
protected async setInstallationPath() {
|
||||
const commandOutput = await this._platformService.runCommand(
|
||||
this.discoveryCommand.command,
|
||||
{
|
||||
workingDirectory: this.discoveryCommand.workingDirectory,
|
||||
additionalEnvironmentVariables: this.discoveryCommand.additionalEnvironmentVariables,
|
||||
sudo: false,
|
||||
ignoreError: false
|
||||
},
|
||||
);
|
||||
if (!commandOutput) {
|
||||
throw new Error(`Install location of tool:'${this.displayName}' could not be discovered`);
|
||||
} else {
|
||||
this._installationPath = commandOutput.split(EOL)[0];
|
||||
}
|
||||
}
|
||||
|
||||
isSameOrNewerThan(version: string): boolean {
|
||||
return this._version ? compare(this._version, new SemVer(version)) >= 0 : false;
|
||||
}
|
||||
|
||||
private _storagePathEnsured: boolean = false;
|
||||
private _status: ToolStatus = ToolStatus.NotInstalled;
|
||||
private _osType: OsType;
|
||||
private _version?: SemVer;
|
||||
private _statusDescription?: string;
|
||||
private _installationPath!: string;
|
||||
|
||||
}
|
||||
|
||||
@@ -14,13 +14,20 @@ export interface IToolsService {
|
||||
}
|
||||
|
||||
export class ToolsService implements IToolsService {
|
||||
private supportedTools: ITool[];
|
||||
private supportedTools: Map<string, ITool>;
|
||||
|
||||
constructor(private _platformService: IPlatformService) {
|
||||
this.supportedTools = [new DockerTool(this._platformService), new AzCliTool(this._platformService), new AzdataTool(this._platformService), new KubeCtlTool(this._platformService)];
|
||||
this.supportedTools = new Map<string, ITool>(
|
||||
[
|
||||
new DockerTool(this._platformService),
|
||||
new AzCliTool(this._platformService),
|
||||
new AzdataTool(this._platformService),
|
||||
new KubeCtlTool(this._platformService)
|
||||
].map<[string, ITool]>((tool: ITool) => [tool.name, tool])
|
||||
);
|
||||
}
|
||||
|
||||
getToolByName(toolName: string): ITool | undefined {
|
||||
return this.supportedTools.find(t => t.name === toolName);
|
||||
return this.supportedTools.get(toolName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,3 +69,4 @@ export const DockerRegistry_VariableName = 'AZDATA_NB_VAR_BDC_REGISTRY';
|
||||
export const DockerImageTag_VariableName = 'AZDATA_NB_VAR_BDC_DOCKER_IMAGE_TAG';
|
||||
export const DockerUsername_VariableName = 'AZDATA_NB_VAR_BDC_DOCKER_USERNAME';
|
||||
export const DockerPassword_VariableName = 'AZDATA_NB_VAR_BDC_DOCKER_PASSWORD';
|
||||
export const ToolsInstallPath = 'AZDATA_NB_VAR_TOOLS_INSTALLATION_PATH';
|
||||
|
||||
@@ -15,10 +15,9 @@ import { ClusterSettingsPage } from './pages/clusterSettingsPage';
|
||||
import { ServiceSettingsPage } from './pages/serviceSettingsPage';
|
||||
import { TargetClusterContextPage } from './pages/targetClusterPage';
|
||||
import { IKubeService } from '../../services/kubeService';
|
||||
import { IAzdataService, BdcEndpoint } from '../../services/azdataService';
|
||||
import { IAzdataService } from '../../services/azdataService';
|
||||
import { DeploymentProfilePage } from './pages/deploymentProfilePage';
|
||||
import { INotebookService } from '../../services/notebookService';
|
||||
import { getErrorMessage, getDateTimeString } from '../../utils';
|
||||
import { DeployClusterWizardModel, AuthenticationMode } from './deployClusterWizardModel';
|
||||
import * as VariableNames from './constants';
|
||||
import * as os from 'os';
|
||||
@@ -28,7 +27,6 @@ const localize = nls.loadMessageBundle();
|
||||
|
||||
export class DeployClusterWizard extends WizardBase<DeployClusterWizard, DeployClusterWizardModel> {
|
||||
private _saveConfigButton: azdata.window.Button;
|
||||
private _scriptToNotebookButton: azdata.window.Button;
|
||||
|
||||
public get kubeService(): IKubeService {
|
||||
return this._kubeService;
|
||||
@@ -42,24 +40,20 @@ export class DeployClusterWizard extends WizardBase<DeployClusterWizard, DeployC
|
||||
return this._notebookService;
|
||||
}
|
||||
|
||||
public get saveConfigButton(): azdata.window.Button {
|
||||
return this._saveConfigButton;
|
||||
public showCustomButtons(): void {
|
||||
this._saveConfigButton.hidden = false;
|
||||
}
|
||||
|
||||
public get scriptToNotebookButton(): azdata.window.Button {
|
||||
return this._scriptToNotebookButton;
|
||||
public hideCustomButtons(): void {
|
||||
this._saveConfigButton.hidden = true;
|
||||
}
|
||||
|
||||
constructor(private wizardInfo: WizardInfo, private _kubeService: IKubeService, private _azdataService: IAzdataService, private _notebookService: INotebookService) {
|
||||
super(DeployClusterWizard.getTitle(wizardInfo.type), new DeployClusterWizardModel(wizardInfo.type));
|
||||
this._saveConfigButton = azdata.window.createButton(localize('deployCluster.SaveConfigFiles', "Save config files"), 'left');
|
||||
this._saveConfigButton.hidden = true;
|
||||
this._scriptToNotebookButton = azdata.window.createButton(localize('deployCluster.ScriptToNotebook', 'Script to Notebook'), 'left');
|
||||
this._scriptToNotebookButton.hidden = true;
|
||||
this.addButton(this._saveConfigButton);
|
||||
this.addButton(this._scriptToNotebookButton);
|
||||
this.registerDisposable(this._saveConfigButton.onClick(() => this.saveConfigFiles()));
|
||||
this.registerDisposable(this._scriptToNotebookButton.onClick(() => this.scriptToNotebook()));
|
||||
}
|
||||
|
||||
public get deploymentType(): BdcDeploymentType {
|
||||
@@ -69,80 +63,14 @@ export class DeployClusterWizard extends WizardBase<DeployClusterWizard, DeployC
|
||||
protected initialize(): void {
|
||||
this.setPages(this.getPages());
|
||||
this.wizardObject.generateScriptButton.hidden = true;
|
||||
this.wizardObject.doneButton.label = localize('deployCluster.Deploy', "Deploy");
|
||||
this.wizardObject.doneButton.label = localize('deployCluster.ScriptToNotebook', "Script to Notebook");
|
||||
}
|
||||
|
||||
protected onCancel(): void {
|
||||
}
|
||||
|
||||
protected onOk(): void {
|
||||
const taskName = localize('resourceDeployment.DeployBDCTask', "Deploy SQL Server Big Data Cluster \"{0}\"", this.model.getStringValue(VariableNames.ClusterName_VariableName));
|
||||
azdata.tasks.startBackgroundOperation({
|
||||
displayName: taskName,
|
||||
description: taskName,
|
||||
isCancelable: false,
|
||||
operation: async op => {
|
||||
op.updateStatus(azdata.TaskStatus.InProgress);
|
||||
const env: NodeJS.ProcessEnv = {};
|
||||
this.setEnvironmentVariables(env);
|
||||
const notebook = await this.notebookService.getNotebook(this.wizardInfo.azdata_notebook);
|
||||
notebook.cells.splice(3, 0, {
|
||||
cell_type: 'code',
|
||||
source: this.model.getCodeCellContentForNotebook(),
|
||||
metadata: {},
|
||||
execution_count: 0,
|
||||
outputs: []
|
||||
});
|
||||
const result = await this.notebookService.executeNotebook(notebook, env);
|
||||
if (result.succeeded) {
|
||||
op.updateStatus(azdata.TaskStatus.Succeeded);
|
||||
const connectToMasterSql = localize('resourceDeployment.ConnectToMasterSQLServer', "Connect to Master SQL Server");
|
||||
const selectedOption = await vscode.window.showInformationMessage(localize('resourceDeployment.DeploymentSucceeded', "Successfully deployed SQL Server Big Data Cluster: {0}",
|
||||
this.model.getStringValue(VariableNames.ClusterName_VariableName)),
|
||||
connectToMasterSql);
|
||||
if (selectedOption === connectToMasterSql) {
|
||||
let endpoints: BdcEndpoint[];
|
||||
try {
|
||||
endpoints = await this.azdataService.getEndpoints(this.model.getStringValue(VariableNames.ClusterName_VariableName)!,
|
||||
this.model.getStringValue(VariableNames.AdminUserName_VariableName)!,
|
||||
this.model.getStringValue(VariableNames.AdminPassword_VariableName)!);
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(localize('resourceDeployment.ErroRetrievingEndpoints', "Failed to retrieve the endpoint list. {0}{1}", os.EOL, getErrorMessage(error)));
|
||||
return;
|
||||
}
|
||||
const sqlEndpoint = endpoints.find(endpoint => endpoint.name === 'sql-server-master');
|
||||
if (sqlEndpoint) {
|
||||
vscode.commands.executeCommand('azdata.connect', {
|
||||
serverName: sqlEndpoint.endpoint,
|
||||
providerName: 'MSSQL',
|
||||
authenticationType: 'SqlLogin',
|
||||
userName: this.model.getStringValue(VariableNames.AdminUserName_VariableName)!,
|
||||
password: this.model.getStringValue(VariableNames.AdminPassword_VariableName)!
|
||||
});
|
||||
} else {
|
||||
vscode.window.showErrorMessage(localize('resourceDeployment.NoSQLEndpointFound', "Master SQL Server endpoint is not found."));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
op.updateStatus(azdata.TaskStatus.Failed, result.errorMessage);
|
||||
if (result.outputNotebook) {
|
||||
const viewErrorDetail = localize('resourceDeployment.ViewErrorDetail', "View error detail");
|
||||
const selectedOption = await vscode.window.showErrorMessage(localize('resourceDeployment.DeployFailed', "Failed to deploy SQL Server Big Data Cluster \"{0}\".",
|
||||
this.model.getStringValue(VariableNames.ClusterName_VariableName)),
|
||||
viewErrorDetail);
|
||||
if (selectedOption === viewErrorDetail) {
|
||||
try {
|
||||
this.notebookService.launchNotebookWithContent(`deploy-${getDateTimeString()}`, result.outputNotebook);
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(localize('resourceDeployment.FailedToOpenNotebook', "An error occured launching the output notebook. {1}{2}.", os.EOL, getErrorMessage(error)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vscode.window.showErrorMessage(localize('resourceDeployment.DeployFailedNoOutputNotebook', "Failed to deploy SQL Server Big Data Cluster and no output notebook was generated."));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.scriptToNotebook();
|
||||
}
|
||||
|
||||
private getPages(): WizardPageBase<DeployClusterWizard>[] {
|
||||
|
||||
@@ -8,6 +8,7 @@ import * as VariableNames from './constants';
|
||||
import { BigDataClusterDeploymentProfile, DataResource, SqlServerMasterResource, HdfsResource } from '../../services/bigDataClusterDeploymentProfile';
|
||||
import { BdcDeploymentType } from '../../interfaces';
|
||||
import { EOL } from 'os';
|
||||
import { delimiter } from 'path';
|
||||
|
||||
export class DeployClusterWizardModel extends Model {
|
||||
constructor(public deploymentTarget: BdcDeploymentType) {
|
||||
@@ -162,6 +163,7 @@ export class DeployClusterWizardModel extends Model {
|
||||
statements.push(`os.environ["DOCKER_USERNAME"] = '${this.getStringValue(VariableNames.DockerUsername_VariableName)}'`);
|
||||
statements.push(`os.environ["DOCKER_PASSWORD"] = os.environ["${VariableNames.DockerPassword_VariableName}"]`);
|
||||
}
|
||||
statements.push(`os.environ["PATH"] = os.environ["PATH"] + "${delimiter}" + "${this.escapeForNotebookCodeCell(process.env[VariableNames.ToolsInstallPath]!)}"`);
|
||||
statements.push(`print('Variables have been set successfully.')`);
|
||||
return statements.map(line => line + EOL);
|
||||
}
|
||||
|
||||
@@ -33,11 +33,10 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
|
||||
if (this.wizard.model.deploymentTarget === BdcDeploymentType.NewAKS) {
|
||||
this.wizard.wizardObject.message = {
|
||||
level: azdata.window.MessageLevel.Information,
|
||||
text: localize('resourceDeployment.NewAKSBrowserWindowPrompt', "A browser window for logging to Azure will be opened during the SQL Server Big Data Cluster deployment.")
|
||||
text: localize('resourceDeployment.NewAKSBrowserWindowPrompt', "A browser window for signing into Azure will be opened during the SQL Server Big Data Cluster deployment.")
|
||||
};
|
||||
}
|
||||
this.wizard.saveConfigButton.hidden = false;
|
||||
this.wizard.scriptToNotebookButton.hidden = false;
|
||||
this.wizard.showCustomButtons();
|
||||
this.formItems.forEach(item => {
|
||||
this.form!.removeFormItem(item);
|
||||
});
|
||||
@@ -311,8 +310,7 @@ export class SummaryPage extends WizardPageBase<DeployClusterWizard> {
|
||||
}
|
||||
|
||||
public onLeave() {
|
||||
this.wizard.saveConfigButton.hidden = true;
|
||||
this.wizard.scriptToNotebookButton.hidden = true;
|
||||
this.wizard.hideCustomButtons();
|
||||
this.wizard.wizardObject.message = { text: '' };
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,12 @@ import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { DialogBase } from './dialogBase';
|
||||
import { INotebookService } from '../services/notebookService';
|
||||
import { DialogInfo, instanceOfNotebookBasedDialogInfo } from '../interfaces';
|
||||
import { DialogInfo, instanceOfNotebookBasedDialogInfo, NotebookBasedDialogInfo } from '../interfaces';
|
||||
import { Validator, initializeDialog, InputComponents, setModelValues } from './modelViewUtils';
|
||||
import { Model } from './model';
|
||||
import { EOL } from 'os';
|
||||
import { getDateTimeString, getErrorMessage } from '../utils';
|
||||
import { IPlatformService } from '../services/platformService';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
@@ -20,9 +22,19 @@ export class DeploymentInputDialog extends DialogBase {
|
||||
private inputComponents: InputComponents = {};
|
||||
|
||||
constructor(private notebookService: INotebookService,
|
||||
private platformService: IPlatformService,
|
||||
private dialogInfo: DialogInfo) {
|
||||
super(dialogInfo.title, dialogInfo.name, false);
|
||||
this._dialogObject.okButton.label = localize('deploymentDialog.OKButtonText', 'Open Notebook');
|
||||
let okButtonText: string;
|
||||
if (dialogInfo.actionText) {
|
||||
okButtonText = dialogInfo.actionText;
|
||||
} else if (instanceOfNotebookBasedDialogInfo(dialogInfo) && !dialogInfo.runNotebook) {
|
||||
okButtonText = localize('deploymentDialog.OpenNotebook', "Open Notebook");
|
||||
} else {
|
||||
okButtonText = localize('deploymentDialog.OkButtonText', "OK");
|
||||
}
|
||||
|
||||
this._dialogObject.okButton.label = okButtonText;
|
||||
}
|
||||
|
||||
protected initialize() {
|
||||
@@ -63,11 +75,52 @@ export class DeploymentInputDialog extends DialogBase {
|
||||
setModelValues(this.inputComponents, model);
|
||||
if (instanceOfNotebookBasedDialogInfo(this.dialogInfo)) {
|
||||
model.setEnvironmentVariables();
|
||||
this.notebookService.launchNotebook(this.dialogInfo.notebook).then(() => { }, (error) => {
|
||||
vscode.window.showErrorMessage(error);
|
||||
});
|
||||
if (this.dialogInfo.runNotebook) {
|
||||
this.executeNotebook(this.dialogInfo);
|
||||
} else {
|
||||
this.notebookService.launchNotebook(this.dialogInfo.notebook).then(() => { }, (error) => {
|
||||
vscode.window.showErrorMessage(error);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
vscode.commands.executeCommand(this.dialogInfo.command, model);
|
||||
}
|
||||
}
|
||||
|
||||
private executeNotebook(notebookDialogInfo: NotebookBasedDialogInfo): void {
|
||||
azdata.tasks.startBackgroundOperation({
|
||||
displayName: notebookDialogInfo.taskName!,
|
||||
description: notebookDialogInfo.taskName!,
|
||||
isCancelable: false,
|
||||
operation: async op => {
|
||||
op.updateStatus(azdata.TaskStatus.InProgress);
|
||||
const notebook = await this.notebookService.getNotebook(notebookDialogInfo.notebook);
|
||||
const result = await this.notebookService.executeNotebook(notebook);
|
||||
if (result.succeeded) {
|
||||
op.updateStatus(azdata.TaskStatus.Succeeded);
|
||||
} else {
|
||||
op.updateStatus(azdata.TaskStatus.Failed, result.errorMessage);
|
||||
if (result.outputNotebook) {
|
||||
const viewErrorDetail = localize('resourceDeployment.ViewErrorDetail', "View error detail");
|
||||
const taskFailedMessage = localize('resourceDeployment.DeployFailed', "The task \"{0}\" has failed.", notebookDialogInfo.taskName);
|
||||
const selectedOption = await vscode.window.showErrorMessage(taskFailedMessage, viewErrorDetail);
|
||||
this.platformService.logToOutputChannel(taskFailedMessage);
|
||||
if (selectedOption === viewErrorDetail) {
|
||||
try {
|
||||
this.notebookService.launchNotebookWithContent(`deploy-${getDateTimeString()}`, result.outputNotebook);
|
||||
} catch (error) {
|
||||
const launchNotebookError = localize('resourceDeployment.FailedToOpenNotebook', "An error occurred launching the output notebook. {1}{2}.", EOL, getErrorMessage(error));
|
||||
this.platformService.logToOutputChannel(launchNotebookError);
|
||||
vscode.window.showErrorMessage(launchNotebookError);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const errorMessage = localize('resourceDeployment.TaskFailedWithNoOutputNotebook', "The task \"{0}\" failed and no output Notebook was generated.", notebookDialogInfo.taskName);
|
||||
this.platformService.logToOutputChannel(errorMessage);
|
||||
vscode.window.showErrorMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@ export abstract class DialogBase {
|
||||
this.dispose();
|
||||
}
|
||||
|
||||
protected onComplete(): void { }
|
||||
protected onComplete(): void {
|
||||
}
|
||||
|
||||
protected dispose(): void {
|
||||
this._toDispose.forEach(disposable => disposable.dispose());
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as nls from 'vscode-nls';
|
||||
import { AgreementInfo, DeploymentProvider, ITool, ResourceType } from '../interfaces';
|
||||
import { IResourceTypeService } from '../services/resourceTypeService';
|
||||
import { IToolsService } from '../services/toolsService';
|
||||
import { getErrorMessage } from '../utils';
|
||||
import { getErrorMessage, setEnvironmentVariablesForInstallPaths } from '../utils';
|
||||
import { DialogBase } from './dialogBase';
|
||||
import { createFlexContainer } from './modelViewUtils';
|
||||
|
||||
@@ -37,7 +37,6 @@ export class ResourceTypePickerDialog extends DialogBase {
|
||||
resourceType: ResourceType) {
|
||||
super(localize('resourceTypePickerDialog.title', "Select the deployment options"), 'ResourceTypePickerDialog', true);
|
||||
this._selectedResourceType = resourceType;
|
||||
this._dialogObject.okButton.onClick(() => this.onComplete());
|
||||
this._installToolButton = azdata.window.createButton(localize('deploymentDialog.InstallToolsButton', "Install tools"));
|
||||
this._toDispose.push(this._installToolButton.onClick(() => {
|
||||
this.installTools();
|
||||
@@ -71,7 +70,7 @@ export class ResourceTypePickerDialog extends DialogBase {
|
||||
this._agreementContainer = view.modelBuilder.divContainer().component();
|
||||
const toolColumn: azdata.TableColumn = {
|
||||
value: localize('deploymentDialog.toolNameColumnHeader', "Tool"),
|
||||
width: 150
|
||||
width: 70
|
||||
};
|
||||
const descriptionColumn: azdata.TableColumn = {
|
||||
value: localize('deploymentDialog.toolDescriptionColumnHeader', "Description"),
|
||||
@@ -79,16 +78,20 @@ export class ResourceTypePickerDialog extends DialogBase {
|
||||
};
|
||||
const installStatusColumn: azdata.TableColumn = {
|
||||
value: localize('deploymentDialog.toolStatusColumnHeader', "Status"),
|
||||
width: 100
|
||||
width: 70
|
||||
};
|
||||
const versionColumn: azdata.TableColumn = {
|
||||
value: localize('deploymentDialog.toolVersionColumnHeader', "Version"),
|
||||
width: 100
|
||||
value: localize('deploymentDialog.toolVersionColumnHeader', "Installed Version"),
|
||||
width: 90
|
||||
};
|
||||
const minVersionColumn: azdata.TableColumn = {
|
||||
value: localize('deploymentDialog.toolMinimumVersionColumnHeader', "Required Version"),
|
||||
width: 90
|
||||
};
|
||||
|
||||
this._toolsTable = view.modelBuilder.table().withProperties<azdata.TableComponentProperties>({
|
||||
data: [],
|
||||
columns: [toolColumn, descriptionColumn, installStatusColumn, versionColumn],
|
||||
columns: [toolColumn, descriptionColumn, installStatusColumn, versionColumn, minVersionColumn],
|
||||
width: tableWidth
|
||||
}).component();
|
||||
|
||||
@@ -216,13 +219,15 @@ export class ResourceTypePickerDialog extends DialogBase {
|
||||
});
|
||||
this._toolsLoadingComponent.loading = true;
|
||||
this._dialogObject.okButton.enabled = false;
|
||||
|
||||
Promise.all(this._tools.map(tool => tool.loadInformation())).then(async () => {
|
||||
Promise.all(
|
||||
this._tools.map(tool => tool.loadInformation())
|
||||
).then(async () => {
|
||||
// If the local timestamp does not match the class level timestamp, it means user has changed options, ignore the results
|
||||
if (this.toolRefreshTimestamp !== currentRefreshTimestamp) {
|
||||
return;
|
||||
}
|
||||
let autoInstallRequired = false;
|
||||
let minVersionCheckFailed = false;
|
||||
const messages: string[] = [];
|
||||
this._toolsTable.data = toolRequirements.map(toolReq => {
|
||||
const tool = this.toolsService.getToolByName(toolReq.name)!;
|
||||
@@ -235,27 +240,42 @@ export class ResourceTypePickerDialog extends DialogBase {
|
||||
if (tool.statusDescription !== undefined) {
|
||||
console.warn(localize('deploymentDialog.DetailToolStatusDescription', "Additional status information for tool: '{0}' [ {1} ]. {2}", tool.name, tool.homePage, tool.statusDescription));
|
||||
}
|
||||
} else if (tool.isInstalled && toolReq.version && !tool.isSameOrNewerThan(toolReq.version)) {
|
||||
minVersionCheckFailed = true;
|
||||
messages.push(localize('deploymentDialog.ToolDoesNotMeetVersionRequirement', "'{0}' [ {1} ] does not meet the minimum version requirement, please uninstall it and restart Azure Data Studio.", tool.displayName, tool.homePage));
|
||||
}
|
||||
|
||||
autoInstallRequired = tool.autoInstallRequired;
|
||||
return [tool.displayName, tool.description, tool.displayStatus, tool.fullVersion || ''];
|
||||
autoInstallRequired = autoInstallRequired || tool.autoInstallRequired;
|
||||
return [tool.displayName, tool.description, tool.displayStatus, tool.fullVersion || '', toolReq.version || ''];
|
||||
});
|
||||
|
||||
this._installToolButton.hidden = !autoInstallRequired;
|
||||
this._dialogObject.okButton.enabled = messages.length === 0 && !autoInstallRequired;
|
||||
if (messages.length !== 0) {
|
||||
messages.push(localize('deploymentDialog.VersionInformationDebugHint', "You will need to restart Azure Data Studio if the tools are installed after Azure Data Studio is launched to pick up the updated PATH environment variable. You may find additional details in the debug console."));
|
||||
if (!minVersionCheckFailed) {
|
||||
messages.push(localize('deploymentDialog.VersionInformationDebugHint', "You will need to restart Azure Data Studio if the tools are installed by yourself after Azure Data Studio is launched to pick up the updated PATH environment variable. You may find additional details in the debug console by running the 'Toggle Developer Tools' command in the Azure Data Studio Command Palette."));
|
||||
}
|
||||
this._dialogObject.message = {
|
||||
level: azdata.window.MessageLevel.Error,
|
||||
text: localize('deploymentDialog.ToolCheckFailed', "Some required tools are not installed."),
|
||||
text: localize('deploymentDialog.ToolCheckFailed', "Some required tools are not installed or do not meet the minimum version requirement."),
|
||||
description: messages.join(EOL)
|
||||
};
|
||||
} else if (autoInstallRequired) {
|
||||
let infoText: string[] = [localize('deploymentDialog.InstallToolsHint', "Some required tools are not installed, you can click the \"{0}\" button to install them.", this._installToolButton.label)];
|
||||
const informationalMessagesArray = this._tools.reduce<string[]>((returnArray, currentTool) => {
|
||||
if (currentTool.needsInstallation) {
|
||||
returnArray.push(...currentTool.dependencyMessages);
|
||||
}
|
||||
return returnArray;
|
||||
}, /* initial Value of return array*/[]);
|
||||
const informationalMessagesSet: Set<string> = new Set<string>(informationalMessagesArray);
|
||||
if (informationalMessagesSet.size > 0) {
|
||||
infoText.push(...informationalMessagesSet.values());
|
||||
}
|
||||
// we don't have scenarios that have mixed type of tools
|
||||
// either we don't support auto install: docker, or we support auto install for all required tools
|
||||
this._dialogObject.message = {
|
||||
level: azdata.window.MessageLevel.Information,
|
||||
text: localize('deploymentDialog.InstallToolsHint', "Some required tools are not installed, you can click the \"{0}\" button to install them.", this._installToolButton.label)
|
||||
level: azdata.window.MessageLevel.Warning,
|
||||
text: infoText.join(EOL)
|
||||
};
|
||||
}
|
||||
this._toolsLoadingComponent.loading = false;
|
||||
@@ -289,13 +309,14 @@ export class ResourceTypePickerDialog extends DialogBase {
|
||||
}
|
||||
|
||||
protected onComplete(): void {
|
||||
setEnvironmentVariablesForInstallPaths(this._tools);
|
||||
this.resourceTypeService.startDeployment(this.getCurrentProvider());
|
||||
}
|
||||
|
||||
public updateToolsDisplayTableData(tool: ITool) {
|
||||
protected updateToolsDisplayTableData(tool: ITool) {
|
||||
this._toolsTable.data = this._toolsTable.data.map(rowData => {
|
||||
if (rowData[0] === tool.displayName) {
|
||||
return [tool.displayName, tool.description, tool.displayStatus, tool.fullVersion || ''];
|
||||
return [tool.displayName, tool.description, tool.displayStatus, tool.fullVersion || '', rowData[4]];
|
||||
} else {
|
||||
return rowData;
|
||||
}
|
||||
@@ -313,16 +334,26 @@ export class ResourceTypePickerDialog extends DialogBase {
|
||||
|
||||
private async installTools(): Promise<void> {
|
||||
this._installToolButton.enabled = false;
|
||||
let i: number = 0;
|
||||
let tool: ITool;
|
||||
try {
|
||||
for (; i < this._tools.length; i++) {
|
||||
if (this._tools[i].needsInstallation) {
|
||||
const toolRequirements = this.getCurrentProvider().requiredTools;
|
||||
for (let i: number = 0; i < toolRequirements.length; i++) {
|
||||
const toolReq = toolRequirements[i];
|
||||
tool = this.toolsService.getToolByName(toolReq.name)!;
|
||||
if (tool.needsInstallation) {
|
||||
// Update the informational message
|
||||
this._dialogObject.message = {
|
||||
level: azdata.window.MessageLevel.Information,
|
||||
text: localize('deploymentDialog.InstallingTool', "Required tool '{0}' [ {1} ] is being installed now.", this._tools[i].displayName, this._tools[i].homePage)
|
||||
text: localize('deploymentDialog.InstallingTool', "Required tool '{0}' [ {1} ] is being installed now.", tool.displayName, tool.homePage)
|
||||
};
|
||||
await this._tools[i].install();
|
||||
if (tool.isInstalled && toolReq.version && !tool.isSameOrNewerThan(toolReq.version)) {
|
||||
throw new Error(
|
||||
localize('deploymentDialog.ToolDoesNotMeetVersionRequirement', "'{0}' [ {1} ] does not meet the minimum version requirement, please uninstall it and restart Azure Data Studio.",
|
||||
tool.displayName, tool.homePage
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Update the informational message
|
||||
@@ -332,7 +363,7 @@ export class ResourceTypePickerDialog extends DialogBase {
|
||||
};
|
||||
this._dialogObject.okButton.enabled = true;
|
||||
} catch (error) {
|
||||
const errorMessage = this._tools[i].statusDescription || getErrorMessage(error);
|
||||
const errorMessage = tool!.statusDescription || getErrorMessage(error);
|
||||
if (errorMessage) {
|
||||
// Let the tooltip status show the errorMessage just shown so that last status is visible even after showError dialogue has been dismissed.
|
||||
this._dialogObject.message = {
|
||||
@@ -340,7 +371,7 @@ export class ResourceTypePickerDialog extends DialogBase {
|
||||
text: errorMessage
|
||||
};
|
||||
}
|
||||
this._tools[i].showOutputChannel(/*preserverFocus*/false);
|
||||
tool!.showOutputChannel(/*preserverFocus*/false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import { ITool, NoteBookEnvironmentVariablePrefix } from './interfaces';
|
||||
import * as path from 'path';
|
||||
import { ToolsInstallPath } from './ui/deployClusterWizard/constants';
|
||||
|
||||
export function getErrorMessage(error: any): string {
|
||||
return (error instanceof Error)
|
||||
@@ -12,3 +15,23 @@ export function getErrorMessage(error: any): string {
|
||||
export function getDateTimeString(): string {
|
||||
return new Date().toISOString().slice(0, 19).replace(/[^0-9]/g, ''); // Take the date time information and only leaving the numbers
|
||||
}
|
||||
|
||||
export function setEnvironmentVariablesForInstallPaths(tools: ITool[]): void {
|
||||
// Use Set class to make sure the collection only contains unique values.
|
||||
let installationPaths: Set<string> = new Set<string>();
|
||||
tools.forEach(t => {
|
||||
if (t.installationPath) {
|
||||
// construct an env variable name with NoteBookEnvironmentVariablePrefix prefix
|
||||
// and tool.name as suffix, making sure of using all uppercase characters and only _ as separator
|
||||
const envVarName: string = `${NoteBookEnvironmentVariablePrefix}${t.name.toUpperCase().replace(/ |-/g, '_')}`;
|
||||
process.env[envVarName] = t.installationPath;
|
||||
installationPaths.add(path.resolve(path.dirname(t.installationPath)));
|
||||
console.log(`setting env var:'${envVarName}' to: '${t.installationPath}'`);
|
||||
}
|
||||
});
|
||||
if (installationPaths.size > 0) {
|
||||
const envVarToolsInstallationPath: string = [...installationPaths.values()].join(path.delimiter);
|
||||
process.env[ToolsInstallPath] = envVarToolsInstallationPath;
|
||||
console.log(`setting env var:'${ToolsInstallPath}' to: '${envVarToolsInstallationPath}'`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
Microsoft SQL Server Schema Compare for Azure Data Studio includes:
|
||||
|
||||
## Schema Compare *(preview)*
|
||||
## Schema Compare
|
||||
The Schema Compare extension provides an easy to use experience to compare .dacpac files and databases and apply the changes from source to target.
|
||||
|
||||
This experience is currently in its initial preview. Please report issues and feature requests [here.](https://github.com/microsoft/azuredatastudio/issues)
|
||||
Please report issues and feature requests [here.](https://github.com/microsoft/azuredatastudio/issues)
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
"name": "schema-compare",
|
||||
"displayName": "%displayName%",
|
||||
"description": "%description%",
|
||||
"version": "0.8.0",
|
||||
"version": "1.0.0",
|
||||
"publisher": "Microsoft",
|
||||
"preview": true,
|
||||
"preview": false,
|
||||
"engines": {
|
||||
"vscode": "^1.25.0",
|
||||
"azdata": ">=1.11.0"
|
||||
"azdata": ">=1.13.0"
|
||||
},
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/extensions/import/Microsoft_SQL_Server_Import_Extension_and_Tools_Import_Flat_File_Preview.docx",
|
||||
"icon": "images/sqlserver.png",
|
||||
@@ -52,7 +52,7 @@
|
||||
"objectExplorer/item/context": [
|
||||
{
|
||||
"command": "schemaCompare.start",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Database",
|
||||
"when": "connectionProvider == MSSQL && nodeType && nodeType == Database && mssql:engineedition != 11",
|
||||
"group": "export"
|
||||
}
|
||||
]
|
||||
@@ -60,7 +60,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-extension-telemetry": "0.0.18",
|
||||
"vscode-nls": "^3.2.1"
|
||||
"vscode-nls": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^5.2.5",
|
||||
|
||||
@@ -15,13 +15,15 @@ import { getTelemetryErrorType, getEndpointName, verifyConnectionAndGetOwnerUri,
|
||||
import { SchemaCompareDialog } from './dialogs/schemaCompareDialog';
|
||||
import { isNullOrUndefined } from 'util';
|
||||
const localize = nls.loadMessageBundle();
|
||||
const diffEditorTitle = localize('schemaCompare.CompareDetailsTitle', 'Compare Details');
|
||||
const applyConfirmation = localize('schemaCompare.ApplyConfirmation', 'Are you sure you want to update the target?');
|
||||
const reCompareToRefeshMessage = localize('schemaCompare.RecompareToRefresh', 'Press Compare to refresh the comparison.');
|
||||
const generateScriptEnabledMessage = localize('schemaCompare.generateScriptEnabledButton', 'Generate script to deploy changes to target');
|
||||
const generateScriptNoChangesMessage = localize('schemaCompare.generateScriptNoChanges', 'No changes to script');
|
||||
const applyEnabledMessage = localize('schemaCompare.applyButtonEnabledTitle', 'Apply changes to target');
|
||||
const applyNoChangesMessage = localize('schemaCompare.applyNoChanges', 'No changes to apply');
|
||||
const diffEditorTitle = localize('schemaCompare.CompareDetailsTitle', "Compare Details");
|
||||
const applyConfirmation = localize('schemaCompare.ApplyConfirmation', "Are you sure you want to update the target?");
|
||||
const reCompareToRefeshMessage = localize('schemaCompare.RecompareToRefresh', "Press Compare to refresh the comparison.");
|
||||
const generateScriptEnabledMessage = localize('schemaCompare.generateScriptEnabledButton', "Generate script to deploy changes to target");
|
||||
const generateScriptNoChangesMessage = localize('schemaCompare.generateScriptNoChanges', "No changes to script");
|
||||
const applyEnabledMessage = localize('schemaCompare.applyButtonEnabledTitle', "Apply changes to target");
|
||||
const applyNoChangesMessage = localize('schemaCompare.applyNoChanges', "No changes to apply");
|
||||
const includeExcludeInfoMessage = localize('schemaCompare.includeExcludeInfoMessage', "Please note that include/exclude operations can take a moment to calculate affected dependencies");
|
||||
|
||||
// Do not localize this, this is used to decide the icon for the editor.
|
||||
// TODO : In future icon should be decided based on language id (scmp) and not resource name
|
||||
const schemaCompareResourceName = 'Schema Compare';
|
||||
@@ -69,6 +71,8 @@ export class SchemaCompareMainWindow {
|
||||
private targetName: string;
|
||||
private scmpSourceExcludes: mssql.SchemaCompareObjectId[];
|
||||
private scmpTargetExcludes: mssql.SchemaCompareObjectId[];
|
||||
private diffEntryRowMap = new Map<string, number>();
|
||||
private showIncludeExcludeWaitingMessage: boolean = true;
|
||||
|
||||
public sourceEndpointInfo: mssql.SchemaCompareEndpointInfo;
|
||||
public targetEndpointInfo: mssql.SchemaCompareEndpointInfo;
|
||||
@@ -101,7 +105,6 @@ export class SchemaCompareMainWindow {
|
||||
this.editor.registerContent(async view => {
|
||||
this.differencesTable = view.modelBuilder.table().withProperties({
|
||||
data: [],
|
||||
height: 300,
|
||||
title: localize('schemaCompare.differencesTableTitle', "Comparison between Source and Target")
|
||||
}).component();
|
||||
|
||||
@@ -343,6 +346,11 @@ export class SchemaCompareMainWindow {
|
||||
if (this.comparisonResult.differences.length > 0) {
|
||||
this.flexModel.addItem(this.splitView);
|
||||
|
||||
// create a map of the differences to row numbers
|
||||
for (let i = 0; i < data.length; ++i) {
|
||||
this.diffEntryRowMap.set(this.createDiffEntryKey(this.comparisonResult.differences[i]), i);
|
||||
}
|
||||
|
||||
// only enable generate script button if the target is a db
|
||||
if (this.targetEndpointInfo.endpointType === mssql.SchemaCompareEndpointType.Database) {
|
||||
this.generateScriptButton.enabled = true;
|
||||
@@ -386,9 +394,54 @@ export class SchemaCompareMainWindow {
|
||||
this.tablelistenersToDispose.push(this.differencesTable.onCellAction(async (rowState) => {
|
||||
let checkboxState = <azdata.ICheckboxCellActionEventArgs>rowState;
|
||||
if (checkboxState) {
|
||||
// show an info notification the first time when trying to exclude to notify the user that it may take some time to calculate affected dependencies
|
||||
if (this.showIncludeExcludeWaitingMessage) {
|
||||
this.showIncludeExcludeWaitingMessage = false;
|
||||
vscode.window.showInformationMessage(includeExcludeInfoMessage);
|
||||
}
|
||||
|
||||
let diff = this.comparisonResult.differences[checkboxState.row];
|
||||
await service.schemaCompareIncludeExcludeNode(this.comparisonResult.operationId, diff, checkboxState.checked, azdata.TaskExecutionMode.execute);
|
||||
this.saveExcludeState(checkboxState);
|
||||
const result = await service.schemaCompareIncludeExcludeNode(this.comparisonResult.operationId, diff, checkboxState.checked, azdata.TaskExecutionMode.execute);
|
||||
let checkboxesToChange = [];
|
||||
if (result.success) {
|
||||
this.saveExcludeState(checkboxState);
|
||||
|
||||
// dependencies could have been included or excluded as a result, so save their exclude states
|
||||
result.affectedDependencies.forEach(difference => {
|
||||
// find the row of the difference and set its checkbox
|
||||
const diffEntryKey = this.createDiffEntryKey(difference);
|
||||
if (this.diffEntryRowMap.has(diffEntryKey)) {
|
||||
const row = this.diffEntryRowMap.get(diffEntryKey);
|
||||
checkboxesToChange.push({ row: row, column: 2, columnName: 'Include', checked: difference.included });
|
||||
const dependencyCheckBoxState: azdata.ICheckboxCellActionEventArgs = {
|
||||
checked: difference.included,
|
||||
row: row,
|
||||
column: 2,
|
||||
columnName: undefined
|
||||
};
|
||||
this.saveExcludeState(dependencyCheckBoxState);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// failed because of dependencies
|
||||
if (result.blockingDependencies) {
|
||||
// show the first dependent that caused this to fail in the warning message
|
||||
const diffEntryName = this.createName(diff.sourceValue ? diff.sourceValue : diff.targetValue);
|
||||
const firstDependentName = this.createName(result.blockingDependencies[0].sourceValue ? result.blockingDependencies[0].sourceValue : result.blockingDependencies[0].targetValue);
|
||||
const cannotExcludeMessage = localize('schemaCompare.cannotExcludeMessage', "Cannot exclude {0}. Included dependents exist such as {1}", diffEntryName, firstDependentName);
|
||||
const cannotIncludeMessage = localize('schemaCompare.cannotIncludeMessage', "Cannot include {0}. Excluded dependents exist such as {1}", diffEntryName, firstDependentName);
|
||||
vscode.window.showWarningMessage(checkboxState.checked ? cannotIncludeMessage : cannotExcludeMessage);
|
||||
} else {
|
||||
vscode.window.showWarningMessage(result.errorMessage);
|
||||
}
|
||||
|
||||
// set checkbox back to previous state
|
||||
checkboxesToChange.push({ row: checkboxState.row, column: checkboxState.column, columnName: 'Include', checked: !checkboxState.checked });
|
||||
}
|
||||
|
||||
if (checkboxesToChange.length > 0) {
|
||||
this.differencesTable.updateCells = checkboxesToChange;
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -396,6 +449,7 @@ export class SchemaCompareMainWindow {
|
||||
// save state based on source name if present otherwise target name (parity with SSDT)
|
||||
private saveExcludeState(rowState: azdata.ICheckboxCellActionEventArgs) {
|
||||
if (rowState) {
|
||||
this.differencesTable.data[rowState.row][2] = rowState.checked;
|
||||
let diff = this.comparisonResult.differences[rowState.row];
|
||||
let key = (diff.sourceValue && diff.sourceValue.length > 0) ? this.createName(diff.sourceValue) : this.createName(diff.targetValue);
|
||||
if (key) {
|
||||
@@ -460,7 +514,6 @@ export class SchemaCompareMainWindow {
|
||||
|
||||
private removeExcludeEntry(collection: mssql.SchemaCompareObjectId[], entryName: string) {
|
||||
if (collection) {
|
||||
console.error('removing ' + entryName);
|
||||
const index = collection.findIndex(e => this.createName(e.nameParts) === entryName);
|
||||
collection.splice(index, 1);
|
||||
}
|
||||
@@ -491,6 +544,10 @@ export class SchemaCompareMainWindow {
|
||||
return nameParts.join('.');
|
||||
}
|
||||
|
||||
private createDiffEntryKey(entry: mssql.DiffEntry): string {
|
||||
return `${this.createName(entry.sourceValue)}_${this.createName(entry.targetValue)}_${entry.updateAction}_${entry.name}`;
|
||||
}
|
||||
|
||||
private getFormattedScript(diffEntry: mssql.DiffEntry, getSourceScript: boolean): string {
|
||||
// if there is no entry, the script has to be \n because an empty string shows up as a difference but \n doesn't
|
||||
if ((getSourceScript && diffEntry.sourceScript === null)
|
||||
@@ -525,6 +582,7 @@ export class SchemaCompareMainWindow {
|
||||
this.flexModel.removeItem(this.startText);
|
||||
this.flexModel.addItem(this.loader, { CSSStyles: { 'margin-top': '30px' } });
|
||||
this.flexModel.addItem(this.waitText, { CSSStyles: { 'margin-top': '30px', 'align-self': 'center' } });
|
||||
this.showIncludeExcludeWaitingMessage = true;
|
||||
this.diffEditor.updateProperties({
|
||||
contentLeft: os.EOL,
|
||||
contentRight: os.EOL,
|
||||
|
||||
@@ -24,7 +24,7 @@ export class SchemaCompareTestService implements mssql.ISchemaCompareService {
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
schemaCompareIncludeExcludeNode(operationId: string, diffEntry: mssql.DiffEntry, IncludeRequest: boolean, taskExecutionMode: azdata.TaskExecutionMode): Thenable<azdata.ResultStatus> {
|
||||
schemaCompareIncludeExcludeNode(operationId: string, diffEntry: mssql.DiffEntry, IncludeRequest: boolean, taskExecutionMode: azdata.TaskExecutionMode): Thenable<mssql.SchemaCompareIncludeExcludeResult> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
|
||||
@@ -1609,10 +1609,10 @@ vscode-extension-telemetry@0.0.18:
|
||||
dependencies:
|
||||
applicationinsights "1.0.1"
|
||||
|
||||
vscode-nls@^3.2.1:
|
||||
version "3.2.5"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-3.2.5.tgz#25520c1955108036dec607c85e00a522f247f1a4"
|
||||
integrity sha512-ITtoh3V4AkWXMmp3TB97vsMaHRgHhsSFPsUdzlueSL+dRZbSNTZeOmdQv60kjCV306ghPxhDeoNUEm3+EZMuyw==
|
||||
vscode-nls@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c"
|
||||
integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A==
|
||||
|
||||
vscode@1.1.26:
|
||||
version "1.1.26"
|
||||
|
||||
@@ -315,6 +315,18 @@
|
||||
{
|
||||
"id": "vscode.yaml",
|
||||
"path": "./translations/extensions/yaml.i18n.json"
|
||||
},
|
||||
{
|
||||
"id": "Microsoft.dacpac",
|
||||
"path": "./translations/extensions/dacpac.i18n.json"
|
||||
},
|
||||
{
|
||||
"id": "Microsoft.schema-compare",
|
||||
"path": "./translations/extensions/schema-compare.i18n.json"
|
||||
},
|
||||
{
|
||||
"id": "Microsoft.notebook",
|
||||
"path": "./translations/extensions/notebook.i18n.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -323,4 +335,4 @@
|
||||
"scripts": {
|
||||
"update": "cd ../vscode && npm run update-localization-extension bg"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,26 +8,26 @@
|
||||
],
|
||||
"version": "1.0.0.0",
|
||||
"contents": {
|
||||
"extensions/dacpac/out/wizard/pages/selectOperationpage": {
|
||||
"out/wizard/pages/selectOperationpage": {
|
||||
"dacFx.deployRadioButtonLabel": "DACPAC-Datei für eine Datenebenenanwendung für eine SQL Server-Instanz bereitstellen [DACPAC bereitstellen]",
|
||||
"dacFx.extractRadioButtonLabel": "Datenebenenanwendung aus einer SQL Server-Instanz in DACPAC-Datei extrahieren [DACPAC extrahieren]",
|
||||
"dacFx.importRadioButtonLabel": "Datenbank aus einer BACPAC-Datei erstellen [BACPAC importieren]",
|
||||
"dacFx.exportRadioButtonLabel": "Schema und Daten aus einer Datenbank in das logische BACPAC-Dateiformat exportieren [BACPAC exportieren]"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/importConfigPage": {
|
||||
"out/wizard/pages/importConfigPage": {
|
||||
"dacFxImport.openFile": "Eröffnungskurs",
|
||||
"dacFxImport.fileTextboxTitle": "Dateispeicherort"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/extractConfigPage": {
|
||||
"out/wizard/pages/extractConfigPage": {
|
||||
"dacfxExtract.saveFile": "Speichern",
|
||||
"dacFxExtract.fileTextboxTitle": "Dateispeicherort",
|
||||
"dacFxExtract.versionTextboxTitle": "Version (Verwenden Sie x.x.x.x, wobei x für eine Zahl steht)"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/exportConfigPage": {
|
||||
"out/wizard/pages/exportConfigPage": {
|
||||
"dacfxExport.saveFile": "Speichern",
|
||||
"dacFxExport.fileTextboxTitle": "Dateispeicherort"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/deployPlanPage": {
|
||||
"out/wizard/pages/deployPlanPage": {
|
||||
"dacfx.dataLossTextWithCount": "{0} der aufgeführten Bereitstellungsaktionen können zu Datenverlust führen. Stellen Sie sicher, dass eine Sicherung oder eine Momentaufnahme vorhanden ist, falls Probleme mit der Bereitstellung auftreten.",
|
||||
"dacFx.dataLossCheckbox": "Vorgang trotz möglicher Datenverluste fortsetzen",
|
||||
"dacfx.noDataLossText": "Die aufgeführten Bereitstellungsaktionen führen nicht zu Datenverlust.",
|
||||
@@ -41,7 +41,7 @@
|
||||
"dacfx.dataLossColumn": "Datenverlust",
|
||||
"dacfx.dataLossTooltip": "Vorgänge, die zu Datenverlusten führen können, werden mit einem Warnhinweis gekennzeichnet."
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/deployConfigPage": {
|
||||
"out/wizard/pages/deployConfigPage": {
|
||||
"dacFx.databaseNameTextBox": "Datenbankname",
|
||||
"dacFx.databaseNameDropdown": "Datenbankname",
|
||||
"dacFxDeploy.openFile": "Eröffnungskurs",
|
||||
@@ -51,7 +51,7 @@
|
||||
"dacFx.targetDatabaseRadioButtonsTitle": "Zieldatenbank",
|
||||
"dacFx.targetDatabaseDropdownTitle": "Datenbankname"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/dacFxSummaryPage": {
|
||||
"out/wizard/pages/dacFxSummaryPage": {
|
||||
"dacfx.targetServerName": "Zielserver",
|
||||
"dacfx.targetDatabaseName": "Zieldatenbank",
|
||||
"dacfx.sourceServerName": "Quellserver",
|
||||
@@ -61,16 +61,16 @@
|
||||
"dacfx.settingColumn": "Einstellung",
|
||||
"dacfx.valueColumn": "Wert"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/api/dacFxConfigPage": {
|
||||
"out/wizard/api/dacFxConfigPage": {
|
||||
"dacFx.targetServerDropdownTitle": "Zielserver",
|
||||
"dacFx.sourceServerDropdownTitle": "Quellserver",
|
||||
"dacFx.databaseNameTextBox": "Zieldatenbank",
|
||||
"dacFx.sourceDatabaseDropdownTitle": "Quelldatenbank"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/api/basePage": {
|
||||
"out/wizard/api/basePage": {
|
||||
"basePage.defaultUser": "Standard"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/dataTierApplicationWizard": {
|
||||
"out/wizard/dataTierApplicationWizard": {
|
||||
"dacFx.selectOperationPageName": "Vorgang auswählen",
|
||||
"dacFx.deployConfigPageName": "Einstellungen für die DACPAC-Bereitstellung festlegen",
|
||||
"dacFx.deployPlanPage": "Bereitstellungsplan überprüfen",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
],
|
||||
"version": "1.0.0.0",
|
||||
"contents": {
|
||||
"extensions/notebook/package": {
|
||||
"package": {
|
||||
"displayName": "Notebook-Kernerweiterungen",
|
||||
"description": "Definiert den datenprokotollbasierten Notebook-Beitrag und viele Notebook-Befehle und -Beiträge.",
|
||||
"notebook.configuration.title": "Notebook-Konfiguration",
|
||||
@@ -37,11 +37,11 @@
|
||||
"title.configurePython": "Python für Notebooks konfigurieren",
|
||||
"title.managePackages": "Pakete verwalten"
|
||||
},
|
||||
"extensions/notebook/out/dialog/managePackages/managePackagesDialog": {
|
||||
"out/dialog/managePackages/managePackagesDialog": {
|
||||
"managePackages.dialogName": "Pakete verwalten",
|
||||
"managePackages.cancelButtonText": "Schließen"
|
||||
},
|
||||
"extensions/notebook/out/dialog/managePackages/installedPackagesTab": {
|
||||
"out/dialog/managePackages/installedPackagesTab": {
|
||||
"managePackages.installedTabTitle": "INSTALLIERT",
|
||||
"managePackages.pkgNameColumn": "Name",
|
||||
"managePackages.newPkgVersionColumn": "Version",
|
||||
@@ -53,7 +53,7 @@
|
||||
"managePackages.backgroundUninstallComplete": "Die Deinstallation für \"{0}\" wurde abgeschlossen.",
|
||||
"managePackages.backgroundUninstallFailed": "Fehler beim Deinstallieren von \"{0}\". Fehler: {1}"
|
||||
},
|
||||
"extensions/notebook/out/dialog/managePackages/addNewPackageTab": {
|
||||
"out/dialog/managePackages/addNewPackageTab": {
|
||||
"managePackages.invalidTextPlaceholder": "N/V",
|
||||
"managePackages.packageNotFound": "Das angegebene Paket wurde nicht gefunden.",
|
||||
"managePackages.searchBarPlaceholder": "{0}-Pakete durchsuchen",
|
||||
@@ -69,31 +69,31 @@
|
||||
"managePackages.backgroundInstallComplete": "Die Installation für {0} {1} wurde abgeschlossen.",
|
||||
"managePackages.backgroundInstallFailed": "Fehler beim Installieren von {0} {1}. Fehler: {2}"
|
||||
},
|
||||
"extensions/notebook/out/protocol/notebookUriHandler": {
|
||||
"out/protocol/notebookUriHandler": {
|
||||
"notebook.unsupportedAction": "Die Aktion \"{0}\" wird für diesen Handler nicht unterstützt.",
|
||||
"unsupportedScheme": "Der Link \"{0}\" kann nicht geöffnet werden, weil nur HTTP- und HTTPS-Links unterstützt werden.",
|
||||
"notebook.confirmOpen": "\"{0}\" herunterladen und öffnen?",
|
||||
"notebook.fileNotFound": "Die angegebene Datei wurde nicht gefunden.",
|
||||
"notebook.fileDownloadError": "Fehler bei der Anforderung zum Öffnen von Dateien: {0} {1}"
|
||||
},
|
||||
"extensions/notebook/out/jupyter/serverInstance": {
|
||||
"out/jupyter/serverInstance": {
|
||||
"serverStopError": "Fehler beim Beenden von Notebook-Server: {0}",
|
||||
"notebookStartProcessExitPremature": "Der Notebook-Vorgang wurde vorzeitig beendet. Fehler: {0}, StdErr-Ausgabe: {1}",
|
||||
"jupyterError": "Von Jupyter gesendeter Fehler: {0}",
|
||||
"jupyterOutputMsgStartSuccessful": "... Jupyter wird bei \"{0}\" ausgeführt.",
|
||||
"jupyterOutputMsgStart": "... Der Notebook-Server wird gestartet."
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterSettingWriter": {
|
||||
"out/jupyter/jupyterSettingWriter": {
|
||||
"UnexpectedSettingType": "Unerwarteter Einstellungstyp \"{0}\"."
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterSessionManager": {
|
||||
"out/jupyter/jupyterSessionManager": {
|
||||
"errorStartBeforeReady": "Eine Sitzung kann nicht gestartet werden, der Manager ist noch nicht initialisiert.",
|
||||
"connectionNotValid": "Für Spark-Kernel ist eine Verbindung mit einer Masterinstanz eines Big Data-Clusters in SQL Server erforderlich."
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterServerManager": {
|
||||
"out/jupyter/jupyterServerManager": {
|
||||
"shutdownError": "Fehler beim Herunterfahren des Notebook-Servers: {0}"
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterServerInstallation": {
|
||||
"out/jupyter/jupyterServerInstallation": {
|
||||
"msgInstallPkgProgress": "Notebook-Abhängigkeiten werden installiert.",
|
||||
"msgPythonDownloadComplete": "Der Python-Download ist abgeschlossen.",
|
||||
"msgPythonDownloadError": "Fehler beim Herunterladen von Python-Setup.",
|
||||
@@ -113,15 +113,15 @@
|
||||
"msgJupyterInstallDone": "... Jupyter-Installation abgeschlossen.",
|
||||
"msgInstallingSpark": "SparkMagic wird installiert..."
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterNotebookProvider": {
|
||||
"out/jupyter/jupyterNotebookProvider": {
|
||||
"errNotebookUriMissing": "Ein Notebook-Pfad ist erforderlich."
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterController": {
|
||||
"out/jupyter/jupyterController": {
|
||||
"notebookFileType": "Notebooks",
|
||||
"unsupportedFileType": "Nur IPYNB-Notebooks werden unterstützt.",
|
||||
"confirmReinstall": "Möchten Sie eine Neuinstallation durchführen?"
|
||||
},
|
||||
"extensions/notebook/out/dialog/configurePythonDialog": {
|
||||
"out/dialog/configurePythonDialog": {
|
||||
"configurePython.dialogName": "Python für Notebooks konfigurieren",
|
||||
"configurePython.okButtonText": "Installieren",
|
||||
"configurePython.cancelButtonText": "Abbrechen",
|
||||
@@ -136,7 +136,7 @@
|
||||
"configurePython.newInstall": "Neue Python-Installation",
|
||||
"configurePython.existingInstall": "Vorhandene Python-Installation verwenden"
|
||||
},
|
||||
"extensions/notebook/out/book/bookTreeView": {
|
||||
"out/book/bookTreeView": {
|
||||
"openNotebookError": "Fehler beim Öffnen der Datei \"{0}\": {1}",
|
||||
"openMarkdownError": "Fehler beim Öffnen der Datei \"{0}\": {1}",
|
||||
"openExternalLinkError": "Fehler beim Öffnen von Link {0}: {1}",
|
||||
@@ -146,16 +146,16 @@
|
||||
"openMarkdownCommand": "Markdown öffnen",
|
||||
"missingFileError": "Fehlende Datei: {0}"
|
||||
},
|
||||
"extensions/notebook/out/common/utils": {
|
||||
"out/common/utils": {
|
||||
"mkdirOutputMsg": "... {0} wird erstellt",
|
||||
"executeCommandProcessExited": "Der Prozess wurde mit Code {0} beendet."
|
||||
},
|
||||
"extensions/notebook/out/common/localizedConstants": {
|
||||
"out/common/localizedConstants": {
|
||||
"msgYes": "Ja",
|
||||
"msgNo": "Nein",
|
||||
"msgSampleCodeDataFrame": "Dieser Beispielcode lädt die Datei in einen Datenrahmen und zeigt die ersten 10 Ergebnisse an."
|
||||
},
|
||||
"extensions/notebook/out/extension": {
|
||||
"out/extension": {
|
||||
"msgSampleCodeDataFrame": "Dieser Beispielcode lädt die Datei in einen Datenrahmen und zeigt die ersten 10 Ergebnisse an.",
|
||||
"noNotebookVisible": "Es ist kein Notebook-Editor aktiv.",
|
||||
"codeCellName": "Code",
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
],
|
||||
"version": "1.0.0.0",
|
||||
"contents": {
|
||||
"extensions/schema-compare/package": {
|
||||
"package": {
|
||||
"displayName": "SQL Server-Schemavergleich",
|
||||
"description": "Der SQL Server-Schemavergleich für Azure Data Studio unterstützt den Vergleich der Schemas von Datenbanken und DACPACs.",
|
||||
"schemaCompare.start": "Schemavergleich"
|
||||
},
|
||||
"extensions/schema-compare/out/dialogs/schemaCompareOptionsDialog": {
|
||||
"out/dialogs/schemaCompareOptionsDialog": {
|
||||
"SchemaCompareOptionsDialog.Ok": "OK",
|
||||
"SchemaCompareOptionsDialog.Cancel": "Abbrechen",
|
||||
"SchemaCompareOptionsDialog.Reset": "Zurücksetzen",
|
||||
@@ -242,7 +242,7 @@
|
||||
"SchemaCompare.Description.DropObjectsNotInSource": "Gibt an, ob Objekte, die nicht in der Datenbankmomentaufnahme-Datei (.dacpac) enthalten sind, beim Veröffentlichen in einer Datenbank aus der Zieldatenbank gelöscht werden. Dieser Wert hat Vorrang vor 'DropExtendedProperties'.",
|
||||
"SchemaCompare.Description.IgnoreColumnOrder": "Gibt an, ob Unterschiede in der Tabellenspaltenreihenfolge beim Veröffentlichen in einer Datenbank ignoriert oder aktualisiert werden sollen."
|
||||
},
|
||||
"extensions/schema-compare/out/dialogs/schemaCompareDialog": {
|
||||
"out/dialogs/schemaCompareDialog": {
|
||||
"schemaCompareDialog.ok": "OK",
|
||||
"schemaCompareDialog.cancel": "Abbrechen",
|
||||
"schemaCompareDialog.SourceTitle": "Quelle",
|
||||
@@ -263,7 +263,7 @@
|
||||
"schemaCompare.openFile": "Eröffnungskurs",
|
||||
"schemaCompareDialog.defaultUser": "Standard"
|
||||
},
|
||||
"extensions/schema-compare/out/schemaCompareMainWindow": {
|
||||
"out/schemaCompareMainWindow": {
|
||||
"schemaCompare.CompareDetailsTitle": "Details vergleichen",
|
||||
"schemaCompare.ApplyConfirmation": "Möchten Sie das Ziel aktualisieren?",
|
||||
"schemaCompare.RecompareToRefresh": "Klicken Sie auf \"Vergleichen\", um den Vergleich zu aktualisieren.",
|
||||
|
||||
@@ -315,6 +315,18 @@
|
||||
{
|
||||
"id": "vscode.yaml",
|
||||
"path": "./translations/extensions/yaml.i18n.json"
|
||||
},
|
||||
{
|
||||
"id": "Microsoft.dacpac",
|
||||
"path": "./translations/extensions/dacpac.i18n.json"
|
||||
},
|
||||
{
|
||||
"id": "Microsoft.schema-compare",
|
||||
"path": "./translations/extensions/schema-compare.i18n.json"
|
||||
},
|
||||
{
|
||||
"id": "Microsoft.notebook",
|
||||
"path": "./translations/extensions/notebook.i18n.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -323,4 +335,4 @@
|
||||
"scripts": {
|
||||
"update": "cd ../vscode && npm run update-localization-extension es"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,26 +8,26 @@
|
||||
],
|
||||
"version": "1.0.0.0",
|
||||
"contents": {
|
||||
"extensions/dacpac/out/wizard/pages/selectOperationpage": {
|
||||
"out/wizard/pages/selectOperationpage": {
|
||||
"dacFx.deployRadioButtonLabel": "Implementa un archivo .dacpa de una aplicación de la capa de datos en una instancia de SQL Server [Implementar Dacpac]",
|
||||
"dacFx.extractRadioButtonLabel": "Extrae una aplicación de la capa de datos de una instancia de SQL Server a un archivo .dacpac [Extraer Dacpac]",
|
||||
"dacFx.importRadioButtonLabel": "Crea una base de datos a partir de un archivo .bacpac [Importar Bacpac]",
|
||||
"dacFx.exportRadioButtonLabel": "Exportar el esquema y los datos de una base de datos al formato de archivo lógico .bacpac [Exportar Bacpac]"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/importConfigPage": {
|
||||
"out/wizard/pages/importConfigPage": {
|
||||
"dacFxImport.openFile": "Abrir",
|
||||
"dacFxImport.fileTextboxTitle": "Ubicación de archivos"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/extractConfigPage": {
|
||||
"out/wizard/pages/extractConfigPage": {
|
||||
"dacfxExtract.saveFile": "Guardar",
|
||||
"dacFxExtract.fileTextboxTitle": "Ubicación de archivos",
|
||||
"dacFxExtract.versionTextboxTitle": "Versión (use x.x.x.x, donde x es un número)"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/exportConfigPage": {
|
||||
"out/wizard/pages/exportConfigPage": {
|
||||
"dacfxExport.saveFile": "Guardar",
|
||||
"dacFxExport.fileTextboxTitle": "Ubicación de archivos"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/deployPlanPage": {
|
||||
"out/wizard/pages/deployPlanPage": {
|
||||
"dacfx.dataLossTextWithCount": "{0} de las acciones de implementación enumeradas pueden dar lugar a la pérdida de datos. Asegúrese de que tiene una copia de seguridad o una instantánea por si hay algún problema con la implementación.",
|
||||
"dacFx.dataLossCheckbox": "Continuar a pesar de la posible pérdida de datos",
|
||||
"dacfx.noDataLossText": "Las acciones de implementación enumeradas no darán lugar a la pérdida de datos.",
|
||||
@@ -41,7 +41,7 @@
|
||||
"dacfx.dataLossColumn": "Pérdida de datos",
|
||||
"dacfx.dataLossTooltip": "Las operaciones que pueden dar lugar a la pérdida de datos se marcan con un signo de advertencia"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/deployConfigPage": {
|
||||
"out/wizard/pages/deployConfigPage": {
|
||||
"dacFx.databaseNameTextBox": "Nombre de la base de datos",
|
||||
"dacFx.databaseNameDropdown": "Nombre de la base de datos",
|
||||
"dacFxDeploy.openFile": "Abrir",
|
||||
@@ -51,7 +51,7 @@
|
||||
"dacFx.targetDatabaseRadioButtonsTitle": "Base de datos destino",
|
||||
"dacFx.targetDatabaseDropdownTitle": "Nombre de la base de datos"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/dacFxSummaryPage": {
|
||||
"out/wizard/pages/dacFxSummaryPage": {
|
||||
"dacfx.targetServerName": "Servidor de destino",
|
||||
"dacfx.targetDatabaseName": "Base de datos destino",
|
||||
"dacfx.sourceServerName": "Servidor de origen",
|
||||
@@ -61,16 +61,16 @@
|
||||
"dacfx.settingColumn": "Valor",
|
||||
"dacfx.valueColumn": "Valor"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/api/dacFxConfigPage": {
|
||||
"out/wizard/api/dacFxConfigPage": {
|
||||
"dacFx.targetServerDropdownTitle": "Servidor de destino",
|
||||
"dacFx.sourceServerDropdownTitle": "Servidor de origen",
|
||||
"dacFx.databaseNameTextBox": "Base de datos destino",
|
||||
"dacFx.sourceDatabaseDropdownTitle": "Base de datos de origen"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/api/basePage": {
|
||||
"out/wizard/api/basePage": {
|
||||
"basePage.defaultUser": "Predeterminado"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/dataTierApplicationWizard": {
|
||||
"out/wizard/dataTierApplicationWizard": {
|
||||
"dacFx.selectOperationPageName": "Seleccione una operación",
|
||||
"dacFx.deployConfigPageName": "Seleccione la configuración de implementación de Dacpac",
|
||||
"dacFx.deployPlanPage": "Revise el plan de implementación",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
],
|
||||
"version": "1.0.0.0",
|
||||
"contents": {
|
||||
"extensions/notebook/package": {
|
||||
"package": {
|
||||
"displayName": "Extensiones principales de Notebook",
|
||||
"description": "Define la contribución de Notebook basada en el protocolo de datos y muchos comandos y contribuciones de Notebook.",
|
||||
"notebook.configuration.title": "Configuración del Notebook",
|
||||
@@ -37,11 +37,11 @@
|
||||
"title.configurePython": "Configurar Python para Notebooks",
|
||||
"title.managePackages": "Administrar paquetes"
|
||||
},
|
||||
"extensions/notebook/out/dialog/managePackages/managePackagesDialog": {
|
||||
"out/dialog/managePackages/managePackagesDialog": {
|
||||
"managePackages.dialogName": "Administrar paquetes",
|
||||
"managePackages.cancelButtonText": "Cerrar"
|
||||
},
|
||||
"extensions/notebook/out/dialog/managePackages/installedPackagesTab": {
|
||||
"out/dialog/managePackages/installedPackagesTab": {
|
||||
"managePackages.installedTabTitle": "INSTALADO",
|
||||
"managePackages.pkgNameColumn": "Nombre",
|
||||
"managePackages.newPkgVersionColumn": "Versión",
|
||||
@@ -53,7 +53,7 @@
|
||||
"managePackages.backgroundUninstallComplete": "Desinstalación completada para {0}",
|
||||
"managePackages.backgroundUninstallFailed": "No se ha podido desinstalar {0}. Error: {1}"
|
||||
},
|
||||
"extensions/notebook/out/dialog/managePackages/addNewPackageTab": {
|
||||
"out/dialog/managePackages/addNewPackageTab": {
|
||||
"managePackages.invalidTextPlaceholder": "N/A",
|
||||
"managePackages.packageNotFound": "No se pudo encontrar el paquete especificado",
|
||||
"managePackages.searchBarPlaceholder": "Buscar {0} paquetes",
|
||||
@@ -69,31 +69,31 @@
|
||||
"managePackages.backgroundInstallComplete": "Instalación completada para {0} {1}",
|
||||
"managePackages.backgroundInstallFailed": "No se pudo instalar {0} {1}. Error: {2}"
|
||||
},
|
||||
"extensions/notebook/out/protocol/notebookUriHandler": {
|
||||
"out/protocol/notebookUriHandler": {
|
||||
"notebook.unsupportedAction": "No se admite la acción {0} para este controlador",
|
||||
"unsupportedScheme": "No se puede abrir el vínculo {0} porque solo se admiten los vínculos HTTP y HTTPS",
|
||||
"notebook.confirmOpen": "¿Descargar y abrir \"{0}\"?",
|
||||
"notebook.fileNotFound": "No se pudo encontrar el archivo especificado",
|
||||
"notebook.fileDownloadError": "Error en la solicitud de apertura de archivo: {0} {1}"
|
||||
},
|
||||
"extensions/notebook/out/jupyter/serverInstance": {
|
||||
"out/jupyter/serverInstance": {
|
||||
"serverStopError": "Error al detener el servidor de Notebook: {0}",
|
||||
"notebookStartProcessExitPremature": "El proceso de Notebook se cerró prematuramente con el error: {0}, Salida de StdErr: {1}",
|
||||
"jupyterError": "Error enviado desde Jupyter: {0}",
|
||||
"jupyterOutputMsgStartSuccessful": "... Jupyter se está ejecutando en {0}",
|
||||
"jupyterOutputMsgStart": "... Iniciando el servidor de Notebook"
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterSettingWriter": {
|
||||
"out/jupyter/jupyterSettingWriter": {
|
||||
"UnexpectedSettingType": "Tipo de configuración inesperado {0}"
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterSessionManager": {
|
||||
"out/jupyter/jupyterSessionManager": {
|
||||
"errorStartBeforeReady": "No se puede iniciar una sesión, el administrador aún no está inicializado",
|
||||
"connectionNotValid": "Los kernels de Spark requieren una conexión a una instancia maestra del clúster de macrodatos de SQL Server."
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterServerManager": {
|
||||
"out/jupyter/jupyterServerManager": {
|
||||
"shutdownError": "Error en el apagado del servidor de Notebook: {0}"
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterServerInstallation": {
|
||||
"out/jupyter/jupyterServerInstallation": {
|
||||
"msgInstallPkgProgress": "La instalación de dependencias de Notebook está en curso",
|
||||
"msgPythonDownloadComplete": "La descarga de Python está completa",
|
||||
"msgPythonDownloadError": "Error al descargar la configuración de Python",
|
||||
@@ -113,15 +113,15 @@
|
||||
"msgJupyterInstallDone": "... Instalación de Jupyter completa.",
|
||||
"msgInstallingSpark": "Instalación de SparkMagic..."
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterNotebookProvider": {
|
||||
"out/jupyter/jupyterNotebookProvider": {
|
||||
"errNotebookUriMissing": "Se requiere una ruta de acceso del bloc de notas"
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterController": {
|
||||
"out/jupyter/jupyterController": {
|
||||
"notebookFileType": "Notebooks",
|
||||
"unsupportedFileType": "Solo se admiten los Notebooks de tipo .ipynb",
|
||||
"confirmReinstall": "¿Está seguro de que desea volver a instalar?"
|
||||
},
|
||||
"extensions/notebook/out/dialog/configurePythonDialog": {
|
||||
"out/dialog/configurePythonDialog": {
|
||||
"configurePython.dialogName": "Configurar Python para Notebooks",
|
||||
"configurePython.okButtonText": "Instalar",
|
||||
"configurePython.cancelButtonText": "Cancelar",
|
||||
@@ -136,7 +136,7 @@
|
||||
"configurePython.newInstall": "Nueva instalación de Python",
|
||||
"configurePython.existingInstall": "Usar la instalación de Python existente"
|
||||
},
|
||||
"extensions/notebook/out/book/bookTreeView": {
|
||||
"out/book/bookTreeView": {
|
||||
"openNotebookError": "Error en la apertura del archivo {0}: {1}",
|
||||
"openMarkdownError": "Error en la apertura del archivo {0}: {1}",
|
||||
"openExternalLinkError": "Error al abrir el vínculo {0}: {1}",
|
||||
@@ -146,16 +146,16 @@
|
||||
"openMarkdownCommand": "Abrir Markdown",
|
||||
"missingFileError": "Falta el archivo: {0}"
|
||||
},
|
||||
"extensions/notebook/out/common/utils": {
|
||||
"out/common/utils": {
|
||||
"mkdirOutputMsg": "... Creando {0}",
|
||||
"executeCommandProcessExited": "Proceso cerrado con el código {0}"
|
||||
},
|
||||
"extensions/notebook/out/common/localizedConstants": {
|
||||
"out/common/localizedConstants": {
|
||||
"msgYes": "Sí",
|
||||
"msgNo": "No",
|
||||
"msgSampleCodeDataFrame": "Este código de ejemplo carga el archivo en un marco de datos y muestra los primeros 10 resultados."
|
||||
},
|
||||
"extensions/notebook/out/extension": {
|
||||
"out/extension": {
|
||||
"msgSampleCodeDataFrame": "Este código de ejemplo carga el archivo en un marco de datos y muestra los primeros 10 resultados.",
|
||||
"noNotebookVisible": "Ningún editor de blocs de notas está activo",
|
||||
"codeCellName": "Código",
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
],
|
||||
"version": "1.0.0.0",
|
||||
"contents": {
|
||||
"extensions/schema-compare/package": {
|
||||
"package": {
|
||||
"displayName": "Comparación de esquemas de SQL Server",
|
||||
"description": "La comparación de esquemas de SQL Server para Azure Data Studio admite la comparación de los esquemas de bases de datos y paquetes DAC.",
|
||||
"schemaCompare.start": "Comparación de esquemas"
|
||||
},
|
||||
"extensions/schema-compare/out/dialogs/schemaCompareOptionsDialog": {
|
||||
"out/dialogs/schemaCompareOptionsDialog": {
|
||||
"SchemaCompareOptionsDialog.Ok": "Aceptar",
|
||||
"SchemaCompareOptionsDialog.Cancel": "Cancelar",
|
||||
"SchemaCompareOptionsDialog.Reset": "Restablecer",
|
||||
@@ -242,7 +242,7 @@
|
||||
"SchemaCompare.Description.DropObjectsNotInSource": "Especifica si los objetos que no existen en el archivo de instantánea de base de datos (.dacpac) se quitarán de la base de datos de destino al publicar en una base de datos. Este valor tiene prioridad sobre DropExtendedProperties.",
|
||||
"SchemaCompare.Description.IgnoreColumnOrder": "Especifica si hay que ignorar las diferencias en el orden de columnas de una tabla o bien hay que actualizarlo al publicarlo en una base de datos."
|
||||
},
|
||||
"extensions/schema-compare/out/dialogs/schemaCompareDialog": {
|
||||
"out/dialogs/schemaCompareDialog": {
|
||||
"schemaCompareDialog.ok": "Aceptar",
|
||||
"schemaCompareDialog.cancel": "Cancelar",
|
||||
"schemaCompareDialog.SourceTitle": "ORIGEN",
|
||||
@@ -263,7 +263,7 @@
|
||||
"schemaCompare.openFile": "Abrir",
|
||||
"schemaCompareDialog.defaultUser": "Predeterminado"
|
||||
},
|
||||
"extensions/schema-compare/out/schemaCompareMainWindow": {
|
||||
"out/schemaCompareMainWindow": {
|
||||
"schemaCompare.CompareDetailsTitle": "Comparar detalles",
|
||||
"schemaCompare.ApplyConfirmation": "¿Está seguro de que desea actualizar el destino?",
|
||||
"schemaCompare.RecompareToRefresh": "Presione Comparar para actualizar la comparación.",
|
||||
|
||||
@@ -315,6 +315,18 @@
|
||||
{
|
||||
"id": "vscode.yaml",
|
||||
"path": "./translations/extensions/yaml.i18n.json"
|
||||
},
|
||||
{
|
||||
"id": "Microsoft.dacpac",
|
||||
"path": "./translations/extensions/dacpac.i18n.json"
|
||||
},
|
||||
{
|
||||
"id": "Microsoft.schema-compare",
|
||||
"path": "./translations/extensions/schema-compare.i18n.json"
|
||||
},
|
||||
{
|
||||
"id": "Microsoft.notebook",
|
||||
"path": "./translations/extensions/notebook.i18n.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -323,4 +335,4 @@
|
||||
"scripts": {
|
||||
"update": "cd ../vscode && npm run update-localization-extension fr"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,26 +8,26 @@
|
||||
],
|
||||
"version": "1.0.0.0",
|
||||
"contents": {
|
||||
"extensions/dacpac/out/wizard/pages/selectOperationpage": {
|
||||
"out/wizard/pages/selectOperationpage": {
|
||||
"dacFx.deployRadioButtonLabel": "Déployer un fichier .dacpac d'application de couche Données sur une instance de SQL Server [Déployer Dacpac]",
|
||||
"dacFx.extractRadioButtonLabel": "Extraire une application de couche Données d'une instance de SQL Server vers un fichier .dacpac [Extraire Dacpac]",
|
||||
"dacFx.importRadioButtonLabel": "Créer une base de données à partir d'un fichier .bacpac [Importer Bacpac]",
|
||||
"dacFx.exportRadioButtonLabel": "Exporter le schéma et les données d'une base de données au format de fichier logique .bacpac [Exporter Bacpac]"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/importConfigPage": {
|
||||
"out/wizard/pages/importConfigPage": {
|
||||
"dacFxImport.openFile": "Ouvrir ",
|
||||
"dacFxImport.fileTextboxTitle": "Emplacement du fichier"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/extractConfigPage": {
|
||||
"out/wizard/pages/extractConfigPage": {
|
||||
"dacfxExtract.saveFile": "Enregistrer",
|
||||
"dacFxExtract.fileTextboxTitle": "Emplacement du fichier",
|
||||
"dacFxExtract.versionTextboxTitle": "Version (utiliser x.x.x.x où x est un nombre)"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/exportConfigPage": {
|
||||
"out/wizard/pages/exportConfigPage": {
|
||||
"dacfxExport.saveFile": "Enregistrer",
|
||||
"dacFxExport.fileTextboxTitle": "Emplacement du fichier"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/deployPlanPage": {
|
||||
"out/wizard/pages/deployPlanPage": {
|
||||
"dacfx.dataLossTextWithCount": "{0} des actions de déploiement listées peuvent entraîner une perte de données. Vérifiez que vous avez une sauvegarde ou un instantané disponible en cas de problème avec le déploiement.",
|
||||
"dacFx.dataLossCheckbox": "Poursuivre malgré le risque de perte de données",
|
||||
"dacfx.noDataLossText": "Aucune perte de données suite aux actions de déploiement listées.",
|
||||
@@ -41,7 +41,7 @@
|
||||
"dacfx.dataLossColumn": "Perte de données",
|
||||
"dacfx.dataLossTooltip": "Les opérations pouvant entraîner une perte des données sont marquées avec un signe d'avertissement"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/deployConfigPage": {
|
||||
"out/wizard/pages/deployConfigPage": {
|
||||
"dacFx.databaseNameTextBox": "Nom de la base de données",
|
||||
"dacFx.databaseNameDropdown": "Nom de la base de données",
|
||||
"dacFxDeploy.openFile": "Ouvrir ",
|
||||
@@ -51,7 +51,7 @@
|
||||
"dacFx.targetDatabaseRadioButtonsTitle": "Base de données cible",
|
||||
"dacFx.targetDatabaseDropdownTitle": "Nom de la base de données"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/pages/dacFxSummaryPage": {
|
||||
"out/wizard/pages/dacFxSummaryPage": {
|
||||
"dacfx.targetServerName": "Serveur cible",
|
||||
"dacfx.targetDatabaseName": "Base de données cible",
|
||||
"dacfx.sourceServerName": "Serveur source",
|
||||
@@ -61,16 +61,16 @@
|
||||
"dacfx.settingColumn": "Paramètre",
|
||||
"dacfx.valueColumn": "Valeur"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/api/dacFxConfigPage": {
|
||||
"out/wizard/api/dacFxConfigPage": {
|
||||
"dacFx.targetServerDropdownTitle": "Serveur cible",
|
||||
"dacFx.sourceServerDropdownTitle": "Serveur source",
|
||||
"dacFx.databaseNameTextBox": "Base de données cible",
|
||||
"dacFx.sourceDatabaseDropdownTitle": "Base de données source "
|
||||
},
|
||||
"extensions/dacpac/out/wizard/api/basePage": {
|
||||
"out/wizard/api/basePage": {
|
||||
"basePage.defaultUser": "Par défaut"
|
||||
},
|
||||
"extensions/dacpac/out/wizard/dataTierApplicationWizard": {
|
||||
"out/wizard/dataTierApplicationWizard": {
|
||||
"dacFx.selectOperationPageName": "Sélectionner une opération",
|
||||
"dacFx.deployConfigPageName": "Sélectionner les paramètres de déploiement Dacpac",
|
||||
"dacFx.deployPlanPage": "Examiner le plan de déploiement",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
],
|
||||
"version": "1.0.0.0",
|
||||
"contents": {
|
||||
"extensions/notebook/package": {
|
||||
"package": {
|
||||
"displayName": "Extensions principales de notebook",
|
||||
"description": "Définit la contribution de notebook basée sur un protocole de données, et plusieurs commandes et contributions de notebook.",
|
||||
"notebook.configuration.title": "Configuration du notebook",
|
||||
@@ -37,11 +37,11 @@
|
||||
"title.configurePython": "Configurer Python pour Notebooks",
|
||||
"title.managePackages": "Gérer les packages"
|
||||
},
|
||||
"extensions/notebook/out/dialog/managePackages/managePackagesDialog": {
|
||||
"out/dialog/managePackages/managePackagesDialog": {
|
||||
"managePackages.dialogName": "Gérer les packages",
|
||||
"managePackages.cancelButtonText": "Fermer"
|
||||
},
|
||||
"extensions/notebook/out/dialog/managePackages/installedPackagesTab": {
|
||||
"out/dialog/managePackages/installedPackagesTab": {
|
||||
"managePackages.installedTabTitle": "INSTALLÉ",
|
||||
"managePackages.pkgNameColumn": "Nom",
|
||||
"managePackages.newPkgVersionColumn": "Version",
|
||||
@@ -53,7 +53,7 @@
|
||||
"managePackages.backgroundUninstallComplete": "Désinstallation effectuée pour {0}",
|
||||
"managePackages.backgroundUninstallFailed": "La désinstallation de {0} a échoué. Erreur : {1}"
|
||||
},
|
||||
"extensions/notebook/out/dialog/managePackages/addNewPackageTab": {
|
||||
"out/dialog/managePackages/addNewPackageTab": {
|
||||
"managePackages.invalidTextPlaceholder": "N/A",
|
||||
"managePackages.packageNotFound": "Package spécifié introuvable",
|
||||
"managePackages.searchBarPlaceholder": "Rechercher dans les packages {0}",
|
||||
@@ -69,31 +69,31 @@
|
||||
"managePackages.backgroundInstallComplete": "Installation de {0} {1} effectuée",
|
||||
"managePackages.backgroundInstallFailed": "L'installation de {0} {1} a échoué. Erreur : {2}"
|
||||
},
|
||||
"extensions/notebook/out/protocol/notebookUriHandler": {
|
||||
"out/protocol/notebookUriHandler": {
|
||||
"notebook.unsupportedAction": "L'action {0} n'est pas prise en charge pour ce gestionnaire",
|
||||
"unsupportedScheme": "Impossible d'ouvrir le lien {0}, car seuls les liens HTTP et HTTPS sont pris en charge",
|
||||
"notebook.confirmOpen": "Télécharger et ouvrir '{0}' ?",
|
||||
"notebook.fileNotFound": "Fichier spécifié introuvable",
|
||||
"notebook.fileDownloadError": "La demande d'ouverture de fichier a échoué avec l'erreur : {0} {1}"
|
||||
},
|
||||
"extensions/notebook/out/jupyter/serverInstance": {
|
||||
"out/jupyter/serverInstance": {
|
||||
"serverStopError": "Erreur d'arrêt du serveur de notebook : {0}",
|
||||
"notebookStartProcessExitPremature": "Le processus du notebook s'est terminé prématurément avec l'erreur : {0}, sortie StdErr : {1}",
|
||||
"jupyterError": "Erreur envoyée par Jupyter : {0}",
|
||||
"jupyterOutputMsgStartSuccessful": "...Jupyter est en cours d'exécution sur {0}",
|
||||
"jupyterOutputMsgStart": "...Démarrage du serveur de notebook"
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterSettingWriter": {
|
||||
"out/jupyter/jupyterSettingWriter": {
|
||||
"UnexpectedSettingType": "Type de paramètre inattendu {0}"
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterSessionManager": {
|
||||
"out/jupyter/jupyterSessionManager": {
|
||||
"errorStartBeforeReady": "Impossible de démarrer une session, le gestionnaire n'est pas encore initialisé",
|
||||
"connectionNotValid": "Les noyaux Spark nécessitent une connexion a une instance maître de cluster Big Data SQL Server."
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterServerManager": {
|
||||
"out/jupyter/jupyterServerManager": {
|
||||
"shutdownError": "L'arrêt du serveur de notebook a échoué : {0}"
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterServerInstallation": {
|
||||
"out/jupyter/jupyterServerInstallation": {
|
||||
"msgInstallPkgProgress": "L'installation des dépendances de notebook est en cours",
|
||||
"msgPythonDownloadComplete": "Le téléchargement de Python est terminé",
|
||||
"msgPythonDownloadError": "Erreur de téléchargement du programme d'installation de Python",
|
||||
@@ -113,15 +113,15 @@
|
||||
"msgJupyterInstallDone": "...Installation de Jupyter effectuée.",
|
||||
"msgInstallingSpark": "Installation de SparkMagic..."
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterNotebookProvider": {
|
||||
"out/jupyter/jupyterNotebookProvider": {
|
||||
"errNotebookUriMissing": "Un chemin de notebook est nécessaire"
|
||||
},
|
||||
"extensions/notebook/out/jupyter/jupyterController": {
|
||||
"out/jupyter/jupyterController": {
|
||||
"notebookFileType": "Notebooks",
|
||||
"unsupportedFileType": "Seuls les notebooks .ipynb sont pris en charge",
|
||||
"confirmReinstall": "Voulez-vous vraiment le réinstaller ?"
|
||||
},
|
||||
"extensions/notebook/out/dialog/configurePythonDialog": {
|
||||
"out/dialog/configurePythonDialog": {
|
||||
"configurePython.dialogName": "Configurer Python pour Notebooks",
|
||||
"configurePython.okButtonText": "Installer",
|
||||
"configurePython.cancelButtonText": "Annuler",
|
||||
@@ -136,7 +136,7 @@
|
||||
"configurePython.newInstall": "Nouvelle installation de Python",
|
||||
"configurePython.existingInstall": "Utiliser l'installation existante de Python"
|
||||
},
|
||||
"extensions/notebook/out/book/bookTreeView": {
|
||||
"out/book/bookTreeView": {
|
||||
"openNotebookError": "L'ouverture du fichier {0} a échoué : {1}",
|
||||
"openMarkdownError": "L'ouverture du fichier {0} a échoué : {1}",
|
||||
"openExternalLinkError": "Échec de l'ouverture du lien {0} : {1}",
|
||||
@@ -146,16 +146,16 @@
|
||||
"openMarkdownCommand": "Ouvrir Markdown",
|
||||
"missingFileError": "Fichier manquant : {0}"
|
||||
},
|
||||
"extensions/notebook/out/common/utils": {
|
||||
"out/common/utils": {
|
||||
"mkdirOutputMsg": "... Création de {0}",
|
||||
"executeCommandProcessExited": "Processus terminé avec le code {0}"
|
||||
},
|
||||
"extensions/notebook/out/common/localizedConstants": {
|
||||
"out/common/localizedConstants": {
|
||||
"msgYes": "Oui",
|
||||
"msgNo": "Non",
|
||||
"msgSampleCodeDataFrame": "Cet exemple de code charge le fichier dans un cadre de données et affiche les 10 premiers résultats."
|
||||
},
|
||||
"extensions/notebook/out/extension": {
|
||||
"out/extension": {
|
||||
"msgSampleCodeDataFrame": "Cet exemple de code charge le fichier dans un cadre de données et affiche les 10 premiers résultats.",
|
||||
"noNotebookVisible": "Aucun éditeur de notebook actif",
|
||||
"codeCellName": "Code",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user