diff --git a/extensions/big-data-cluster/images/aks.svg b/extensions/big-data-cluster/images/aks.svg deleted file mode 100644 index 1df4bc8835..0000000000 --- a/extensions/big-data-cluster/images/aks.svg +++ /dev/null @@ -1 +0,0 @@ -AKS \ No newline at end of file diff --git a/extensions/big-data-cluster/images/kubernetes.svg b/extensions/big-data-cluster/images/kubernetes.svg deleted file mode 100644 index 0b17a4013a..0000000000 --- a/extensions/big-data-cluster/images/kubernetes.svg +++ /dev/null @@ -1 +0,0 @@ -Kubernetes \ No newline at end of file diff --git a/extensions/big-data-cluster/package.json b/extensions/big-data-cluster/package.json index 24225c938c..62ee5b7376 100644 --- a/extensions/big-data-cluster/package.json +++ b/extensions/big-data-cluster/package.json @@ -1,94 +1,111 @@ { - "name": "big-data-cluster", - "displayName": "SQL Server big data cluster", - "description": "SQL Server big data cluster", - "version": "0.0.1", - "publisher": "Microsoft", - "preview": true, - "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt", - "icon": "images/sqlserver.png", - "aiKey": "AIF-5574968e-856d-40d2-af67-c89a14e76412", - "engines": { - "vscode": "*", - "azdata": ">=1.4.0" - }, - "activationEvents": [ - "*" - ], - "main": "./out/main", - "repository": { - "type": "git", - "url": "https://github.com/Microsoft/azuredatastudio.git" - }, - "extensionDependencies": [ - "Microsoft.mssql" - ], - "contributes": { - "configuration": { - "type": "object", - "title": "Kubernetes configuration", - "properties": { - "mssql-bdc": { - "type": "object", - "description": "Kubernetes configuration", - "properties": { - "mssql-bdc.kubectl-path": { - "type": "string", - "description": "File path to a kubectl binary." - }, - "mssql-bdc.kubectl-path.windows": { - "type": "string", - "description": "File path to a kubectl binary." - }, - "mssql-bdc.kubectl-path.mac": { - "type": "string", - "description": "File path to a kubectl binary." - }, - "mssql-bdc.kubectl-path.linux": { - "type": "string", - "description": "File path to a kubectl binary." - }, - "mssql-bdc.kubeconfig": { - "type": "string", - "description": "File path to the kubeconfig file." - }, - "mssql-bdc.knownKubeconfigs": { - "type": "array", - "description": "File paths to kubeconfig files from which you can select." - }, - "mssql-bdc.outputFormat": { - "enum": [ - "json", - "yaml" - ], - "type": "string", - "description": "Output format for Kubernetes specs. One of 'json' or 'yaml' (default)." - } - }, - "default": { - "mssql-bdc.namespace": "", - "mssql-bdc.kubectl-path": "", - "mssql-bdc.kubeconfig": "", - "mssql-bdc.knownKubeconfigs": [] - } - } - } - }, - "commands": [ - { - "command": "mssql.cluster.create", - "title": "Create SQL Server big data cluster", - "category": "SQL Server" - } - ] - }, - "dependencies": { - "vscode-nls": "^3.2.1", - "download": "^6.2.5", - "shelljs": "^0.8.3" - }, - "devDependencies": { - "mocha-junit-reporter": "^1.17.0", - "mocha-multi-reporters": "^1.1.7" - } + "name": "big-data-cluster", + "displayName": "%text.sqlServerBigDataClusters%", + "description": "%description%", + "version": "0.0.1", + "publisher": "Microsoft", + "preview": true, + "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt", + "icon": "images/sqlserver.png", + "engines": { + "vscode": "*", + "azdata": "*" + }, + "activationEvents": [ + "*" + ], + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/azuredatastudio.git" + }, + "main": "./out/extension", + "contributes": { + "dataExplorer": { + "sqlBigDataCluster": [ + { + "id": "sqlBigDataCluster", + "name": "%text.sqlServerBigDataClusters%" + } + ] + }, + "menus": { + "commandPalette": [ + { + "command": "bigDataClusters.command.addController", + "when": "false" + }, + { + "command": "bigDataClusters.command.deleteController", + "when": "false" + }, + { + "command": "bigDataClusters.command.refreshController", + "when": "false" + } + ], + "view/title": [ + { + "command": "bigDataClusters.command.addController", + "when": "view == sqlBigDataCluster", + "group": "navigation" + } + ], + "view/item/context": [ + { + "command": "bigDataClusters.command.deleteController", + "when": "viewItem == bigDataClusters.itemType.controllerNode", + "group": "navigation@1" + }, + { + "command": "bigDataClusters.command.refreshController", + "when": "viewItem == bigDataClusters.itemType.controllerNode", + "group": "navigation@1" + } + ] + }, + "configuration": { + "type": "object", + "title": "%text.sqlServerBigDataClusters%", + "properties": { + "clusterControllers.controllers": { + "type": "array" + } + } + }, + "commands": [ + { + "command": "bigDataClusters.command.addController", + "title": "%command.addController.title%", + "icon": { + "light": "resources/light/add.svg", + "dark": "resources/dark/add_inverse.svg" + } + }, + { + "command": "bigDataClusters.command.deleteController", + "title": "%command.deleteController.title%", + "when": "viewItem == bigDataClusters.itemType.controllerNode" + }, + { + "command": "bigDataClusters.command.refreshController", + "title": "%command.refreshController.title%", + "icon": { + "light": "resources/light/refresh.svg", + "dark": "resources/dark/refresh_inverse.svg" + } + } + ] + }, + "dependencies": { + "request": "^2.88.0", + "vscode-nls": "^4.0.0" + }, + "devDependencies": { + "@types/mocha": "^5.2.5", + "@types/node": "^8.0.24", + "mocha": "^5.2.0", + "should": "^13.2.1", + "typemoq": "^2.1.0", + "vscode": "^1.1.26" + } } diff --git a/extensions/big-data-cluster/package.nls.json b/extensions/big-data-cluster/package.nls.json new file mode 100644 index 0000000000..2dc924eda7 --- /dev/null +++ b/extensions/big-data-cluster/package.nls.json @@ -0,0 +1,7 @@ +{ + "description": "Support for managing SQL Server Big Data Clusters", + "text.sqlServerBigDataClusters": "SQL Server Big Data Clusters", + "command.addController.title": "Connect to Controller", + "command.deleteController.title" : "Delete", + "command.refreshController.title" : "Refresh" +} \ No newline at end of file diff --git a/extensions/big-data-cluster/resources/dark/add_inverse.svg b/extensions/big-data-cluster/resources/dark/add_inverse.svg new file mode 100644 index 0000000000..7d3fd77ffd --- /dev/null +++ b/extensions/big-data-cluster/resources/dark/add_inverse.svg @@ -0,0 +1 @@ +add \ No newline at end of file diff --git a/extensions/big-data-cluster/resources/dark/bigDataCluster_controller.svg b/extensions/big-data-cluster/resources/dark/bigDataCluster_controller.svg new file mode 100644 index 0000000000..428b8af38c --- /dev/null +++ b/extensions/big-data-cluster/resources/dark/bigDataCluster_controller.svg @@ -0,0 +1 @@ +centralmanagement_server_16x \ No newline at end of file diff --git a/extensions/big-data-cluster/resources/dark/folder_inverse.svg b/extensions/big-data-cluster/resources/dark/folder_inverse.svg new file mode 100644 index 0000000000..f94d427cb1 --- /dev/null +++ b/extensions/big-data-cluster/resources/dark/folder_inverse.svg @@ -0,0 +1 @@ +folder_inverse_16x16 \ No newline at end of file diff --git a/extensions/big-data-cluster/resources/dark/refresh_inverse.svg b/extensions/big-data-cluster/resources/dark/refresh_inverse.svg new file mode 100644 index 0000000000..d79fdaa4e8 --- /dev/null +++ b/extensions/big-data-cluster/resources/dark/refresh_inverse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/big-data-cluster/resources/dark/sql_bigdata_cluster_inverse.svg b/extensions/big-data-cluster/resources/dark/sql_bigdata_cluster_inverse.svg new file mode 100644 index 0000000000..dbc823e9f0 --- /dev/null +++ b/extensions/big-data-cluster/resources/dark/sql_bigdata_cluster_inverse.svg @@ -0,0 +1,46 @@ + + + + + + image/svg+xml + + sql_bigdata_cluster + + + + + + + sql_bigdata_cluster + + + + diff --git a/extensions/big-data-cluster/resources/light/add.svg b/extensions/big-data-cluster/resources/light/add.svg new file mode 100644 index 0000000000..2b679663e8 --- /dev/null +++ b/extensions/big-data-cluster/resources/light/add.svg @@ -0,0 +1 @@ +add \ No newline at end of file diff --git a/extensions/big-data-cluster/resources/light/bigDataCluster_controller.svg b/extensions/big-data-cluster/resources/light/bigDataCluster_controller.svg new file mode 100644 index 0000000000..428b8af38c --- /dev/null +++ b/extensions/big-data-cluster/resources/light/bigDataCluster_controller.svg @@ -0,0 +1 @@ +centralmanagement_server_16x \ No newline at end of file diff --git a/extensions/big-data-cluster/resources/light/folder.svg b/extensions/big-data-cluster/resources/light/folder.svg new file mode 100644 index 0000000000..8442363a76 --- /dev/null +++ b/extensions/big-data-cluster/resources/light/folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/big-data-cluster/resources/light/refresh.svg b/extensions/big-data-cluster/resources/light/refresh.svg new file mode 100644 index 0000000000..e034574819 --- /dev/null +++ b/extensions/big-data-cluster/resources/light/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/big-data-cluster/resources/light/sql_bigdata_cluster.svg b/extensions/big-data-cluster/resources/light/sql_bigdata_cluster.svg new file mode 100644 index 0000000000..fcf1133186 --- /dev/null +++ b/extensions/big-data-cluster/resources/light/sql_bigdata_cluster.svg @@ -0,0 +1,45 @@ + + + + + + image/svg+xml + + sql_bigdata_cluster + + + + + + + sql_bigdata_cluster + + + + diff --git a/extensions/big-data-cluster/src/bigDataCluster/constants.ts b/extensions/big-data-cluster/src/bigDataCluster/constants.ts new file mode 100644 index 0000000000..cf1ddfee2e --- /dev/null +++ b/extensions/big-data-cluster/src/bigDataCluster/constants.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * 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 { + controllerRoot = 'bigDataClusters.itemType.controllerRootNode', + controller = 'bigDataClusters.itemType.controllerNode', + folder = 'bigDataClusters.itemType.folderNode', + sqlMaster = 'bigDataClusters.itemType.sqlMasterNode', + EndPoint = 'bigDataClusters.itemType.endPointNode', + addController = 'bigDataClusters.itemType.addControllerNode' +} + +export class IconPath { + private static extensionContext: vscode.ExtensionContext; + + public static controllerNode: { dark: string, light: string }; + public static folderNode: { dark: string, light: string }; + public static sqlMasterNode: { dark: string, light: string }; + + public static setExtensionContext(extensionContext: vscode.ExtensionContext) { + IconPath.extensionContext = extensionContext; + IconPath.controllerNode = { + dark: IconPath.extensionContext.asAbsolutePath('resources/dark/bigDataCluster_controller.svg'), + light: IconPath.extensionContext.asAbsolutePath('resources/light/bigDataCluster_controller.svg') + }; + IconPath.folderNode = { + dark: IconPath.extensionContext.asAbsolutePath('resources/dark/folder_inverse.svg'), + light: IconPath.extensionContext.asAbsolutePath('resources/light/folder.svg') + }; + IconPath.sqlMasterNode = { + dark: IconPath.extensionContext.asAbsolutePath('resources/dark/sql_bigdata_cluster_inverse.svg'), + light: IconPath.extensionContext.asAbsolutePath('resources/light/sql_bigdata_cluster.svg') + }; + } +} \ No newline at end of file diff --git a/extensions/big-data-cluster/src/bigDataCluster/controller/apiGenerated.ts b/extensions/big-data-cluster/src/bigDataCluster/controller/apiGenerated.ts new file mode 100644 index 0000000000..15b2fbba13 --- /dev/null +++ b/extensions/big-data-cluster/src/bigDataCluster/controller/apiGenerated.ts @@ -0,0 +1,1654 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as request from 'request'; +import * as http from 'http'; + +let defaultBasePath = 'https://localhost'; + +let primitives = [ + 'string', + 'boolean', + 'double', + 'integer', + 'long', + 'float', + 'number', + 'any' +]; + +class ObjectSerializer { + + public static findCorrectType(data: any, expectedType: string) { + if (data === undefined) { + return expectedType; + } else if (primitives.indexOf(expectedType.toLowerCase()) !== -1) { + return expectedType; + } else if (expectedType === 'Date') { + return expectedType; + } else { + if (enumsMap[expectedType]) { + return expectedType; + } + + if (!typeMap[expectedType]) { + return expectedType; // w/e we don't know the type + } + + // Check the discriminator + let discriminatorProperty = typeMap[expectedType].discriminator; + if (discriminatorProperty === null || discriminatorProperty === undefined) { + return expectedType; // the type does not have a discriminator. use it. + } else { + if (data[discriminatorProperty]) { + return data[discriminatorProperty]; // use the type given in the discriminator + } else { + return expectedType; // discriminator was not present (or an empty string) + } + } + } + } + + public static serialize(data: any, type: string) { + if (data === undefined) { + return data; + } else if (primitives.indexOf(type.toLowerCase()) !== -1) { + return data; + } else if (type.lastIndexOf('Array<', 0) === 0) { // string.startsWith pre es6 + let subType: string = type.replace('Array<', ''); // Array => Type> + subType = subType.substring(0, subType.length - 1); // Type> => Type + let transformedData: any[] = []; + for (let index in data) { + let date = data[index]; + transformedData.push(ObjectSerializer.serialize(date, subType)); + } + return transformedData; + } else if (type === 'Date') { + return data.toString(); + } else { + if (enumsMap[type]) { + return data; + } + if (!typeMap[type]) { // in case we don't know the type + return data; + } + + // get the map for the correct type. + let attributeTypes = typeMap[type].getAttributeTypeMap(); + let instance: { [index: string]: any } = {}; + for (let index in attributeTypes) { + let attributeType = attributeTypes[index]; + instance[attributeType.baseName] = ObjectSerializer.serialize(data[attributeType.name], attributeType.type); + } + return instance; + } + } + + public static deserialize(data: any, type: string) { + // polymorphism may change the actual type. + type = ObjectSerializer.findCorrectType(data, type); + if (data === undefined) { + return data; + } else if (primitives.indexOf(type.toLowerCase()) !== -1) { + return data; + } else if (type.lastIndexOf('Array<', 0) === 0) { // string.startsWith pre es6 + let subType: string = type.replace('Array<', ''); // Array => Type> + subType = subType.substring(0, subType.length - 1); // Type> => Type + let transformedData: any[] = []; + for (let index in data) { + let date = data[index]; + transformedData.push(ObjectSerializer.deserialize(date, subType)); + } + return transformedData; + } else if (type === 'Date') { + return new Date(data); + } else { + if (enumsMap[type]) {// is Enum + return data; + } + + if (!typeMap[type]) { // dont know the type + return data; + } + let instance = new typeMap[type](); + let attributeTypes = typeMap[type].getAttributeTypeMap(); + for (let index in attributeTypes) { + let attributeType = attributeTypes[index]; + instance[attributeType.name] = ObjectSerializer.deserialize(data[attributeType.baseName], attributeType.type); + } + return instance; + } + } +} + +export class AppMetadata { + 'name'?: string; + 'version'?: string; + 'description'?: string; + 'runtimeType'?: string; + 'initCode'?: string; + 'code'?: string; + 'azureFileSpec'?: Array; + 'inputDef'?: Array; + 'outputDef'?: Array; + 'replicas'?: number; + 'poolSizePerReplica'?: number; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + 'name': 'name', + 'baseName': 'name', + 'type': 'string' + }, + { + 'name': 'version', + 'baseName': 'version', + 'type': 'string' + }, + { + 'name': 'description', + 'baseName': 'description', + 'type': 'string' + }, + { + 'name': 'runtimeType', + 'baseName': 'runtimeType', + 'type': 'string' + }, + { + 'name': 'initCode', + 'baseName': 'initCode', + 'type': 'string' + }, + { + 'name': 'code', + 'baseName': 'code', + 'type': 'string' + }, + { + 'name': 'azureFileSpec', + 'baseName': 'azureFileSpec', + 'type': 'Array' + }, + { + 'name': 'inputDef', + 'baseName': 'inputDef', + 'type': 'Array' + }, + { + 'name': 'outputDef', + 'baseName': 'outputDef', + 'type': 'Array' + }, + { + 'name': 'replicas', + 'baseName': 'replicas', + 'type': 'number' + }, + { + 'name': 'poolSizePerReplica', + 'baseName': 'poolSizePerReplica', + 'type': 'number' + } + ]; + + static getAttributeTypeMap() { + return AppMetadata.attributeTypeMap; + } +} + +export class AppModel { + 'name'?: string; + 'internalName'?: string; + 'version'?: string; + 'inputParamDefs'?: Array; + 'outputParamDefs'?: Array; + 'state'?: string; + 'links'?: { [key: string]: string; }; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + 'name': 'name', + 'baseName': 'name', + 'type': 'string' + }, + { + 'name': 'internalName', + 'baseName': 'internal_name', + 'type': 'string' + }, + { + 'name': 'version', + 'baseName': 'version', + 'type': 'string' + }, + { + 'name': 'inputParamDefs', + 'baseName': 'input_param_defs', + 'type': 'Array' + }, + { + 'name': 'outputParamDefs', + 'baseName': 'output_param_defs', + 'type': 'Array' + }, + { + 'name': 'state', + 'baseName': 'state', + 'type': 'string' + }, + { + 'name': 'links', + 'baseName': 'links', + 'type': '{ [key: string]: string; }' + } + ]; + + static getAttributeTypeMap() { + return AppModel.attributeTypeMap; + } +} + +export class AppModelParameterDefinition { + 'name'?: string; + 'type'?: string; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + 'name': 'name', + 'baseName': 'name', + 'type': 'string' + }, + { + 'name': 'type', + 'baseName': 'type', + 'type': 'string' + } + ]; + + static getAttributeTypeMap() { + return AppModelParameterDefinition.attributeTypeMap; + } +} + +export class AppRequest { + 'metadata'?: AppMetadata; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + 'name': 'metadata', + 'baseName': 'metadata', + 'type': 'AppMetadata' + } + ]; + + static getAttributeTypeMap() { + return AppRequest.attributeTypeMap; + } +} + +export class AzureFileSpecDefinition { + 'secretName'?: string; + 'mountPath'?: string; + 'fileShareName'?: string; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + 'name': 'secretName', + 'baseName': 'secretName', + 'type': 'string' + }, + { + 'name': 'mountPath', + 'baseName': 'mountPath', + 'type': 'string' + }, + { + 'name': 'fileShareName', + 'baseName': 'fileShareName', + 'type': 'string' + } + ]; + + static getAttributeTypeMap() { + return AzureFileSpecDefinition.attributeTypeMap; + } +} + +export class EndpointModel { + 'name'?: string; + 'description'?: string; + 'endpoint'?: string; + 'ip'?: string; + 'port'?: number; + 'path'?: string; + 'protocol'?: string; + 'service'?: string; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + 'name': 'name', + 'baseName': 'name', + 'type': 'string' + }, + { + 'name': 'description', + 'baseName': 'description', + 'type': 'string' + }, + { + 'name': 'endpoint', + 'baseName': 'endpoint', + 'type': 'string' + }, + { + 'name': 'ip', + 'baseName': 'ip', + 'type': 'string' + }, + { + 'name': 'port', + 'baseName': 'port', + 'type': 'number' + }, + { + 'name': 'path', + 'baseName': 'path', + 'type': 'string' + }, + { + 'name': 'protocol', + 'baseName': 'protocol', + 'type': 'string' + }, + { + 'name': 'service', + 'baseName': 'service', + 'type': 'string' + } + ]; + + static getAttributeTypeMap() { + return EndpointModel.attributeTypeMap; + } +} + +export class ParameterDefinition { + 'name'?: string; + 'type'?: string; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + 'name': 'name', + 'baseName': 'name', + 'type': 'string' + }, + { + 'name': 'type', + 'baseName': 'type', + 'type': 'string' + } + ]; + + static getAttributeTypeMap() { + return ParameterDefinition.attributeTypeMap; + } +} + +export class TokenModel { + 'tokenType'?: string; + 'accessToken'?: string; + 'expiresIn'?: number; + 'expiresOn'?: number; + 'tokenId'?: string; + + static discriminator: string | undefined = undefined; + + static attributeTypeMap: Array<{ name: string, baseName: string, type: string }> = [ + { + 'name': 'tokenType', + 'baseName': 'token_type', + 'type': 'string' + }, + { + 'name': 'accessToken', + 'baseName': 'access_token', + 'type': 'string' + }, + { + 'name': 'expiresIn', + 'baseName': 'expires_in', + 'type': 'number' + }, + { + 'name': 'expiresOn', + 'baseName': 'expires_on', + 'type': 'number' + }, + { + 'name': 'tokenId', + 'baseName': 'token_id', + 'type': 'string' + } + ]; + + static getAttributeTypeMap() { + return TokenModel.attributeTypeMap; + } +} + + +let enumsMap: { [index: string]: any } = { +}; + +let typeMap: { [index: string]: any } = { + 'AppMetadata': AppMetadata, + 'AppModel': AppModel, + 'AppModelParameterDefinition': AppModelParameterDefinition, + 'AppRequest': AppRequest, + 'AzureFileSpecDefinition': AzureFileSpecDefinition, + 'EndpointModel': EndpointModel, + 'ParameterDefinition': ParameterDefinition, + 'TokenModel': TokenModel, +}; + +export interface Authentication { + /** + * Apply authentication settings to header and query params. + */ + applyToRequest(requestOptions: request.Options): void; +} + +export class HttpBasicAuth implements Authentication { + public username: string = ''; + public password: string = ''; + + applyToRequest(requestOptions: request.Options): void { + requestOptions.auth = { + username: this.username, password: this.password + }; + } +} + +export class ApiKeyAuth implements Authentication { + public apiKey: string = ''; + + constructor(private location: string, private paramName: string) { + } + + applyToRequest(requestOptions: request.Options): void { + if (this.location === 'query') { + (requestOptions.qs)[this.paramName] = this.apiKey; + } else if (this.location === 'header' && requestOptions && requestOptions.headers) { + requestOptions.headers[this.paramName] = this.apiKey; + } + } +} + +export class OAuth implements Authentication { + public accessToken: string = ''; + + applyToRequest(requestOptions: request.Options): void { + if (requestOptions && requestOptions.headers) { + requestOptions.headers['Authorization'] = 'Bearer ' + this.accessToken; + } + } +} + +export class VoidAuth implements Authentication { + public username: string = ''; + public password: string = ''; + + applyToRequest(_: request.Options): void { + // Do nothing + } +} + +export enum AppRouterApiApiKeys { +} + +export class AppRouterApi { + protected _basePath = defaultBasePath; + protected defaultHeaders: any = {}; + protected _useQuerystring: boolean = false; + + protected authentications = { + 'default': new VoidAuth(), + 'basic': new HttpBasicAuth() + }; + + constructor(basePath?: string); + constructor(username: string, password: string, basePath?: string); + constructor(basePathOrUsername: string, password?: string, basePath?: string) { + if (password) { + this.username = basePathOrUsername; + this.password = password; + if (basePath) { + this.basePath = basePath; + } + } else { + if (basePathOrUsername) { + this.basePath = basePathOrUsername; + } + } + } + + set useQuerystring(value: boolean) { + this._useQuerystring = value; + } + + set basePath(basePath: string) { + this._basePath = basePath; + } + + get basePath() { + return this._basePath; + } + + public setDefaultAuthentication(auth: Authentication) { + this.authentications.default = auth; + } + + public setApiKey(key: AppRouterApiApiKeys, value: string) { + (this.authentications as any)[AppRouterApiApiKeys[key]].apiKey = value; + } + set username(username: string) { + this.authentications.basic.username = username; + } + + set password(password: string) { + this.authentications.basic.password = password; + } + + /** + * @param name name + * @param version version + * @param [options] Override http request options. + */ + public appByNameByVersionDelete(name: string, version: string, options: any = {}): Promise<{ response: http.IncomingMessage; body?: any; }> { + const localVarPath = this.basePath + '/app/{name}/{version}' + .replace('{name}', encodeURIComponent(String(name))) + .replace('{version}', encodeURIComponent(String(version))); + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + // verify required parameter 'name' is not null or undefined + if (name === null || name === undefined) { + throw new Error('Required parameter name was null or undefined when calling appByNameByVersionDelete.'); + } + + // verify required parameter 'version' is not null or undefined + if (version === null || version === undefined) { + throw new Error('Required parameter version was null or undefined when calling appByNameByVersionDelete.'); + } + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + let localVarRequestOptions: request.Options = { + method: 'DELETE', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body?: any; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } + + /** + * @param name name + * @param version version + * @param [options] Override http request options. + */ + public appByNameByVersionGet(name: string, version: string, options: any = {}): Promise<{ response: http.IncomingMessage; body: AppModel; }> { + const localVarPath = this.basePath + '/app/{name}/{version}' + .replace('{name}', encodeURIComponent(String(name))) + .replace('{version}', encodeURIComponent(String(version))); + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + // verify required parameter 'name' is not null or undefined + if (name === null || name === undefined) { + throw new Error('Required parameter name was null or undefined when calling appByNameByVersionGet.'); + } + + // verify required parameter 'version' is not null or undefined + if (version === null || version === undefined) { + throw new Error('Required parameter version was null or undefined when calling appByNameByVersionGet.'); + } + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + let localVarRequestOptions: request.Options = { + method: 'GET', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body: AppModel; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + body = ObjectSerializer.deserialize(body, 'AppModel'); + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } + + /** + * @param name name + * @param version version + * @param appRequest appRequest + * @param [options] Override http request options. + */ + public appByNameByVersionPatch(name: string, version: string, appRequest?: AppRequest, options: any = {}): Promise<{ response: http.IncomingMessage; body?: any; }> { + const localVarPath = this.basePath + '/app/{name}/{version}' + .replace('{name}', encodeURIComponent(String(name))) + .replace('{version}', encodeURIComponent(String(version))); + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + // verify required parameter 'name' is not null or undefined + if (name === null || name === undefined) { + throw new Error('Required parameter name was null or undefined when calling appByNameByVersionPatch.'); + } + + // verify required parameter 'version' is not null or undefined + if (version === null || version === undefined) { + throw new Error('Required parameter version was null or undefined when calling appByNameByVersionPatch.'); + } + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + let localVarRequestOptions: request.Options = { + method: 'PATCH', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + body: ObjectSerializer.serialize(appRequest, 'AppRequest') + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body?: any; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } + + /** + * @param name name + * @param version version + * @param [options] Override http request options. + */ + public appByNameByVersionSwaggerJsonGet(name: string, version: string, options: any = {}): Promise<{ response: http.IncomingMessage; body?: any; }> { + const localVarPath = this.basePath + '/app/{name}/{version}/swagger.json' + .replace('{name}', encodeURIComponent(String(name))) + .replace('{version}', encodeURIComponent(String(version))); + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + // verify required parameter 'name' is not null or undefined + if (name === null || name === undefined) { + throw new Error('Required parameter name was null or undefined when calling appByNameByVersionSwaggerJsonGet.'); + } + + // verify required parameter 'version' is not null or undefined + if (version === null || version === undefined) { + throw new Error('Required parameter version was null or undefined when calling appByNameByVersionSwaggerJsonGet.'); + } + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + let localVarRequestOptions: request.Options = { + method: 'GET', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body?: any; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } + + /** + * @param name name + * @param [options] Override http request options. + */ + public appByNameGet(name: string, options: any = {}): Promise<{ response: http.IncomingMessage; body: Array; }> { + const localVarPath = this.basePath + '/app/{name}' + .replace('{name}', encodeURIComponent(String(name))); + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + // verify required parameter 'name' is not null or undefined + if (name === null || name === undefined) { + throw new Error('Required parameter name was null or undefined when calling appByNameGet.'); + } + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + let localVarRequestOptions: request.Options = { + method: 'GET', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body: Array; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + body = ObjectSerializer.deserialize(body, 'Array'); + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } + + /** + * @param [options] Override http request options. + */ + public appGet(options: any = {}): Promise<{ response: http.IncomingMessage; body: Array; }> { + const localVarPath = this.basePath + '/app'; + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + let localVarRequestOptions: request.Options = { + method: 'GET', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body: Array; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + body = ObjectSerializer.deserialize(body, 'Array'); + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } + + /** + * @param _package Zip archive file which contains a spec.yaml + * @param [options] Override http request options. + */ + public createApp(_package: Buffer, options: any = {}): Promise<{ response: http.IncomingMessage; body?: any; }> { + const localVarPath = this.basePath + '/app'; + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + // verify required parameter '_package' is not null or undefined + if (_package === null || _package === undefined) { + throw new Error('Required parameter _package was null or undefined when calling createApp.'); + } + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + if (_package !== undefined) { + localVarFormParams['package'] = _package; + } + localVarUseFormData = true; + + let localVarRequestOptions: request.Options = { + method: 'POST', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body?: any; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } + + /** + * @param _package Zip archive file which contains a spec.yaml + * @param [options] Override http request options. + */ + public updateApp(_package: Buffer, options: any = {}): Promise<{ response: http.IncomingMessage; body?: any; }> { + const localVarPath = this.basePath + '/app'; + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + // verify required parameter '_package' is not null or undefined + if (_package === null || _package === undefined) { + throw new Error('Required parameter _package was null or undefined when calling updateApp.'); + } + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + if (_package !== undefined) { + localVarFormParams['package'] = _package; + } + localVarUseFormData = true; + + let localVarRequestOptions: request.Options = { + method: 'PATCH', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body?: any; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } +} +export enum CertificateRouterApiApiKeys { +} + +export class CertificateRouterApi { + protected _basePath = defaultBasePath; + protected defaultHeaders: any = {}; + protected _useQuerystring: boolean = false; + + protected authentications = { + 'default': new VoidAuth(), + 'basic': new HttpBasicAuth() + }; + + constructor(basePath?: string); + constructor(username: string, password: string, basePath?: string); + constructor(basePathOrUsername: string, password?: string, basePath?: string) { + if (password) { + this.username = basePathOrUsername; + this.password = password; + if (basePath) { + this.basePath = basePath; + } + } else { + if (basePathOrUsername) { + this.basePath = basePathOrUsername; + } + } + } + + set useQuerystring(value: boolean) { + this._useQuerystring = value; + } + + set basePath(basePath: string) { + this._basePath = basePath; + } + + get basePath() { + return this._basePath; + } + + public setDefaultAuthentication(auth: Authentication) { + this.authentications.default = auth; + } + + public setApiKey(key: CertificateRouterApiApiKeys, value: string) { + (this.authentications as any)[CertificateRouterApiApiKeys[key]].apiKey = value; + } + set username(username: string) { + this.authentications.basic.username = username; + } + + set password(password: string) { + this.authentications.basic.password = password; + } + + /** + * @param [options] Override http request options. + */ + public certificatesRootcaGet(options: any = {}): Promise<{ response: http.IncomingMessage; body?: any; }> { + const localVarPath = this.basePath + '/certificates/rootca'; + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + let localVarRequestOptions: request.Options = { + method: 'GET', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body?: any; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } +} + +export enum EndpointRouterApiApiKeys { +} + +export class EndpointRouterApi { + protected _url = defaultBasePath; + protected _defaultHeaders: any = {}; + protected _useQuerystring: boolean = false; + protected _ignoreSslVerification: boolean = false; + + protected _authentications = { + 'default': new VoidAuth(), + 'basic': new HttpBasicAuth() + }; + + constructor(url?: string); + constructor(username: string, password: string, url?: string); + constructor(urlOrUsername: string, password?: string, url?: string) { + if (password) { + this.username = urlOrUsername; + this.password = password; + if (url) { + this.url = url; + } + } else { + if (urlOrUsername) { + this.url = urlOrUsername; + } + } + } + + set useQuerystring(value: boolean) { + this._useQuerystring = value; + } + + set ignoreSslVerification(ignoreSslVerification: boolean) { + this._ignoreSslVerification = ignoreSslVerification; + } + + set url(url: string) { + this._url = url; + } + + get url() { + return this._url; + } + + public setDefaultAuthentication(auth: Authentication) { + this._authentications.default = auth; + } + + public setApiKey(key: EndpointRouterApiApiKeys, value: string) { + (this._authentications as any)[EndpointRouterApiApiKeys[key]].apiKey = value; + } + + set username(username: string) { + this._authentications.basic.username = username; + } + + set password(password: string) { + this._authentications.basic.password = password; + } + + /** + * @param [options] Override http request options. + */ + public endpointsGet(options: request.Options = {}): Promise<{ response: http.IncomingMessage; body: Array; }> { + return this.endpointsGetInternal(undefined, options); + } + + /** + * @param name endpoint name, + * @param [options] Override http request options. + */ + public endpointsByNameGet(name: string, options: request.Options = {}): Promise<{ response: http.IncomingMessage; body: Array; }> { + // verify required parameter 'name' is not null or undefined + if (name === null || name === undefined) { + throw new Error('Required parameter name was null or undefined when calling endpointsByNameGet.'); + } + return this.endpointsGetInternal(name, options); + } + + private endpointsGetInternal(name?: string, options: request.Options = {}): Promise<{ response: http.IncomingMessage; body: Array; }> { + const targetUri = this.url + '/endpoints' + (name ? `/${name}` : ''); + let requestOptions: request.Options = { + method: 'GET', + qs: {}, + headers: {}, + uri: targetUri, + useQuerystring: this._useQuerystring, + json: true, + agentOptions: { + rejectUnauthorized: !this._ignoreSslVerification + } + }; + if (Object.keys(options).length) { + requestOptions = Object.assign(requestOptions, options); + } + + this._authentications.basic.applyToRequest(requestOptions); + this._authentications.default.applyToRequest(requestOptions); + + return new Promise<{ response: http.IncomingMessage; body: Array; }>((resolve, reject) => { + request(requestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + body = ObjectSerializer.deserialize(body, 'Array'); + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } +} + +export enum FileRouterApiApiKeys { +} + +export class FileRouterApi { + protected _basePath = defaultBasePath; + protected defaultHeaders: any = {}; + protected _useQuerystring: boolean = false; + + protected authentications = { + 'default': new VoidAuth(), + 'basic': new HttpBasicAuth() + }; + + constructor(basePath?: string); + constructor(username: string, password: string, basePath?: string); + constructor(basePathOrUsername: string, password?: string, basePath?: string) { + if (password) { + this.username = basePathOrUsername; + this.password = password; + if (basePath) { + this.basePath = basePath; + } + } else { + if (basePathOrUsername) { + this.basePath = basePathOrUsername; + } + } + } + + set useQuerystring(value: boolean) { + this._useQuerystring = value; + } + + set basePath(basePath: string) { + this._basePath = basePath; + } + + get basePath() { + return this._basePath; + } + + public setDefaultAuthentication(auth: Authentication) { + this.authentications.default = auth; + } + + public setApiKey(key: FileRouterApiApiKeys, value: string) { + (this.authentications as any)[FileRouterApiApiKeys[key]].apiKey = value; + } + set username(username: string) { + this.authentications.basic.username = username; + } + + set password(password: string) { + this.authentications.basic.password = password; + } + + /** + * @param filePath filePath + * @param op op + * @param [options] Override http request options. + */ + public filesByFilePathGet(filePath: string, op?: string, options: any = {}): Promise<{ response: http.IncomingMessage; body?: any; }> { + const localVarPath = this.basePath + '/files/{filePath}' + .replace('{filePath}', encodeURIComponent(String(filePath))); + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + // verify required parameter 'filePath' is not null or undefined + if (filePath === null || filePath === undefined) { + throw new Error('Required parameter filePath was null or undefined when calling filesByFilePathGet.'); + } + + if (op !== undefined) { + localVarQueryParameters['op'] = ObjectSerializer.serialize(op, 'string'); + } + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + let localVarRequestOptions: request.Options = { + method: 'GET', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body?: any; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } +} +export enum HealthRouterApiApiKeys { +} + +export class HealthRouterApi { + protected _basePath = defaultBasePath; + protected defaultHeaders: any = {}; + protected _useQuerystring: boolean = false; + + protected authentications = { + 'default': new VoidAuth(), + 'basic': new HttpBasicAuth() + }; + + constructor(basePath?: string); + constructor(username: string, password: string, basePath?: string); + constructor(basePathOrUsername: string, password?: string, basePath?: string) { + if (password) { + this.username = basePathOrUsername; + this.password = password; + if (basePath) { + this.basePath = basePath; + } + } else { + if (basePathOrUsername) { + this.basePath = basePathOrUsername; + } + } + } + + set useQuerystring(value: boolean) { + this._useQuerystring = value; + } + + set basePath(basePath: string) { + this._basePath = basePath; + } + + get basePath() { + return this._basePath; + } + + public setDefaultAuthentication(auth: Authentication) { + this.authentications.default = auth; + } + + public setApiKey(key: HealthRouterApiApiKeys, value: string) { + (this.authentications as any)[HealthRouterApiApiKeys[key]].apiKey = value; + } + set username(username: string) { + this.authentications.basic.username = username; + } + + set password(password: string) { + this.authentications.basic.password = password; + } + + /** + * @param query query + * @param [options] Override http request options. + */ + public healthGet(query?: string, options: any = {}): Promise<{ response: http.IncomingMessage; body?: any; }> { + const localVarPath = this.basePath + '/health'; + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + if (query !== undefined) { + localVarQueryParameters['query'] = ObjectSerializer.serialize(query, 'string'); + } + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + let localVarRequestOptions: request.Options = { + method: 'GET', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body?: any; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } + + /** + * @param [options] Override http request options. + */ + public healthPost(options: any = {}): Promise<{ response: http.IncomingMessage; body?: any; }> { + const localVarPath = this.basePath + '/health'; + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + let localVarRequestOptions: request.Options = { + method: 'POST', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body?: any; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } +} + +export enum TokenRouterApiApiKeys { +} + +export class TokenRouterApi { + protected _basePath = defaultBasePath; + protected defaultHeaders: any = {}; + protected _useQuerystring: boolean = false; + + protected authentications = { + 'default': new VoidAuth(), + 'basic': new HttpBasicAuth() + }; + + constructor(basePath?: string); + constructor(username: string, password: string, basePath?: string); + constructor(basePathOrUsername: string, password?: string, basePath?: string) { + if (password) { + this.username = basePathOrUsername; + this.password = password; + if (basePath) { + this.basePath = basePath; + } + } else { + if (basePathOrUsername) { + this.basePath = basePathOrUsername; + } + } + } + + set useQuerystring(value: boolean) { + this._useQuerystring = value; + } + + set basePath(basePath: string) { + this._basePath = basePath; + } + + get basePath() { + return this._basePath; + } + + public setDefaultAuthentication(auth: Authentication) { + this.authentications.default = auth; + } + + public setApiKey(key: TokenRouterApiApiKeys, value: string) { + (this.authentications as any)[TokenRouterApiApiKeys[key]].apiKey = value; + } + set username(username: string) { + this.authentications.basic.username = username; + } + + set password(password: string) { + this.authentications.basic.password = password; + } + + /** + * @param [options] Override http request options. + */ + public tokenPost(options: any = {}): Promise<{ response: http.IncomingMessage; body: TokenModel; }> { + const localVarPath = this.basePath + '/token'; + let localVarQueryParameters: any = {}; + let localVarHeaderParams: any = (Object).assign({}, this.defaultHeaders); + let localVarFormParams: any = {}; + + localVarHeaderParams = (Object).assign(localVarHeaderParams, options.headers); + + let localVarUseFormData = false; + + let localVarRequestOptions: request.Options = { + method: 'POST', + qs: localVarQueryParameters, + headers: localVarHeaderParams, + uri: localVarPath, + useQuerystring: this._useQuerystring, + json: true, + }; + + this.authentications.basic.applyToRequest(localVarRequestOptions); + + this.authentications.default.applyToRequest(localVarRequestOptions); + + if (Object.keys(localVarFormParams).length) { + if (localVarUseFormData) { + (localVarRequestOptions).formData = localVarFormParams; + } else { + localVarRequestOptions.form = localVarFormParams; + } + } + return new Promise<{ response: http.IncomingMessage; body: TokenModel; }>((resolve, reject) => { + request(localVarRequestOptions, (error, response, body) => { + if (error) { + reject(error); + } else { + body = ObjectSerializer.deserialize(body, 'TokenModel'); + if (response.statusCode && response.statusCode >= 200 && response.statusCode <= 299) { + resolve({ response: response, body: body }); + } else { + reject({ response: response, body: body }); + } + } + }); + }); + } +} diff --git a/extensions/big-data-cluster/src/bigDataCluster/controller/clusterControllerApi.ts b/extensions/big-data-cluster/src/bigDataCluster/controller/clusterControllerApi.ts new file mode 100644 index 0000000000..ce90c7904f --- /dev/null +++ b/extensions/big-data-cluster/src/bigDataCluster/controller/clusterControllerApi.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { EndpointRouterApi } from './apiGenerated'; + +export async function getEndPoints( + url: string, username: string, password: string, ignoreSslVerification?: boolean +): Promise { + + if (!url || !username || !password) { + return undefined; + } + + url = adjustUrl(url); + let endPointApi = new EndpointRouterApi(username, password, url); + endPointApi.ignoreSslVerification = !!ignoreSslVerification; + + let controllerResponse: IEndPointsResponse = undefined; + let controllerError: IControllerError = undefined; + let request = { + url: url, + username: username, + password: password, + method: 'endPointsGet' + }; + + try { + let result = await endPointApi.endpointsGet(); + controllerResponse = { + response: result.response as IHttpResponse, + endPoints: result.body as IEndPoint[], + request + }; + return controllerResponse; + } catch (error) { + if ('response' in error) { + let err: IEndPointsResponse = error as IEndPointsResponse; + let errCode = `${err.response.statusCode || ''}`; + let errMessage = err.response.statusMessage; + let errUrl = err.response.url; + controllerError = { + address: errUrl, + code: errCode, + errno: errCode, + message: errMessage, + name: undefined + }; + } else { + controllerError = error as IControllerError; + } + throw Object.assign(controllerError, { request }) as IControllerError; + } +} + +/** + * Fixes missing protocol and wrong character for port entered by user + */ +function adjustUrl(url: string): string { + if (!url) { + return undefined; + } + + url = url.trim().replace(/ /g, '').replace(/,(\d+)$/, ':$1'); + if (!url.includes('://')) { + url = `https://${url}`; + } + return url; +} + +export interface IEndPointsRequest { + url: string; + username: string; + password?: string; + method?: string; +} + +export interface IEndPointsResponse { + request?: IEndPointsRequest; + response: IHttpResponse; + endPoints: IEndPoint[]; +} + +export interface IHttpResponse { + method?: string; + url?: string; + statusCode?: number; + statusMessage?: string; +} + +export interface IEndPoint { + name?: string; + description?: string; + endpoint?: string; + ip?: string; + port?: number; +} + +export interface IControllerError extends Error { + code?: string; + errno?: string; + message: string; + request?: any; +} diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/addControllerDialog.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/addControllerDialog.ts new file mode 100644 index 0000000000..b09d358c9d --- /dev/null +++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/addControllerDialog.ts @@ -0,0 +1,157 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * 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'; +import { IEndPoint, IControllerError, getEndPoints } from '../controller/clusterControllerApi'; +import { ControllerTreeDataProvider } from '../tree/controllerTreeDataProvider'; +import { TreeNode } from '../tree/treeNode'; +import { showErrorMessage } from '../utils'; + +const localize = nls.loadMessageBundle(); + +export class AddControllerDialogModel { + constructor( + public treeDataProvider: ControllerTreeDataProvider, + public node?: TreeNode, + public prefilledUrl?: string, + public prefilledUsername?: string, + public prefilledPassword?: string, + public prefilledRememberPassword?: boolean + ) { + this.prefilledUrl = prefilledUrl || (node && node['url']); + this.prefilledUsername = prefilledUsername || (node && node['username']); + this.prefilledPassword = prefilledPassword || (node && node['password']); + this.prefilledRememberPassword = prefilledRememberPassword || (node && node['rememberPassword']); + } + + public async onComplete(url: string, username: string, password: string, rememberPassword: boolean): Promise { + let response = await getEndPoints(url, username, password, true); + if (response && response.request) { + let masterInstance: IEndPoint = undefined; + if (response.endPoints) { + masterInstance = response.endPoints.find(e => e.name && e.name === 'sql-server-master'); + } + this.treeDataProvider.addController(response.request.url, response.request.username, + response.request.password, rememberPassword, masterInstance); + await this.treeDataProvider.saveControllers(); + } + } + + public async onError(error: IControllerError): Promise { + // implement + } + + public async onCancel(): Promise { + if (this.node) { + this.node.refresh(); + } + } +} + +export class AddControllerDialog { + + private dialog: azdata.window.Dialog; + private uiModelBuilder: azdata.ModelBuilder; + + private urlInputBox: azdata.InputBoxComponent; + private usernameInputBox: azdata.InputBoxComponent; + private passwordInputBox: azdata.InputBoxComponent; + private rememberPwCheckBox: azdata.CheckBoxComponent; + + constructor(private model: AddControllerDialogModel) { + } + + public showDialog(): void { + this.createDialog(); + azdata.window.openDialog(this.dialog); + } + + private createDialog(): void { + this.dialog = azdata.window.createModelViewDialog(localize('textAddNewController', 'Add New Controller')); + this.dialog.registerContent(async view => { + this.uiModelBuilder = view.modelBuilder; + + this.urlInputBox = this.uiModelBuilder.inputBox() + .withProperties({ + placeHolder: localize('textUrlLower', 'url'), + value: this.model.prefilledUrl + }).component(); + this.usernameInputBox = this.uiModelBuilder.inputBox() + .withProperties({ + placeHolder: localize('textUsernameLower', 'username'), + value: this.model.prefilledUsername + }).component(); + this.passwordInputBox = this.uiModelBuilder.inputBox() + .withProperties({ + placeHolder: localize('textPasswordLower', 'password'), + inputType: 'password', + value: this.model.prefilledPassword + }) + .component(); + this.rememberPwCheckBox = this.uiModelBuilder.checkBox() + .withProperties({ + label: localize('textRememberPassword', 'Remember Password'), + checked: this.model.prefilledRememberPassword + }).component(); + + let formModel = this.uiModelBuilder.formContainer() + .withFormItems([{ + components: [{ + component: this.urlInputBox, + title: localize('textUrlCapital', 'URL'), + required: true + }, { + component: this.usernameInputBox, + title: localize('textUsernameCapital', 'Username'), + required: true + }, { + component: this.passwordInputBox, + title: localize('textPasswordCapital', 'Password'), + required: true + }, { + component: this.rememberPwCheckBox, + title: '' + } + ], + title: '' + }]).withLayout({ width: '100%' }).component(); + + await view.initializeModel(formModel); + }); + + this.dialog.registerCloseValidator(async () => await this.validate()); + this.dialog.cancelButton.onClick(async () => await this.cancel()); + this.dialog.okButton.label = localize('textAdd', 'Add'); + this.dialog.cancelButton.label = localize('textCancel', 'Cancel'); + } + + private async validate(): Promise { + let url = this.urlInputBox && this.urlInputBox.value; + let username = this.usernameInputBox && this.usernameInputBox.value; + let password = this.passwordInputBox && this.passwordInputBox.value; + let rememberPassword = this.passwordInputBox && !!this.rememberPwCheckBox.checked; + + try { + await this.model.onComplete(url, username, password, rememberPassword); + return true; + } catch (error) { + showErrorMessage(error); + if (this.model && this.model.onError) { + await this.model.onError(error as IControllerError); + } + return false; + } + } + + private async cancel(): Promise { + if (this.model && this.model.onCancel) { + await this.model.onCancel(); + } + } +} diff --git a/extensions/big-data-cluster/src/bigDataCluster/tree/addControllerTreeNode.ts b/extensions/big-data-cluster/src/bigDataCluster/tree/addControllerTreeNode.ts new file mode 100644 index 0000000000..73397636a4 --- /dev/null +++ b/extensions/big-data-cluster/src/bigDataCluster/tree/addControllerTreeNode.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vscode-nls'; +import * as azdata from 'azdata'; +import * as vscode from 'vscode'; +import { TreeNode } from './treeNode'; +import { BdcItemType } from '../constants'; + +const localize = nls.loadMessageBundle(); + +export class AddControllerNode extends TreeNode { + private readonly nodeType: string; + + constructor() { + super(localize('textBigDataClusterControllerWithDots', 'Add Big Data Cluster Controller...')); + this.nodeType = BdcItemType.addController; + } + + public async getChildren(): Promise { + return []; + } + + public getTreeItem(): vscode.TreeItem { + let item = new vscode.TreeItem(this.label, vscode.TreeItemCollapsibleState.None); + item.command = { + title: localize('textConnectToController', 'Connect to Controller'), + command: 'bigDataClusters.command.addController', + arguments: [this] + }; + item.contextValue = this.nodeType; + return item; + } + + public getNodeInfo(): azdata.NodeInfo { + return { + label: this.label, + isLeaf: this.isLeaf, + errorMessage: undefined, + metadata: undefined, + nodePath: this.nodePath, + nodeStatus: undefined, + nodeType: this.nodeType, + iconType: this.nodeType, + nodeSubType: undefined + }; + } +} diff --git a/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeChangeHandler.ts b/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeChangeHandler.ts new file mode 100644 index 0000000000..2fc7e30472 --- /dev/null +++ b/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeChangeHandler.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { TreeNode } from './treeNode'; + +export interface IControllerTreeChangeHandler { + notifyNodeChanged(node?: TreeNode): void; +} diff --git a/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeDataProvider.ts b/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeDataProvider.ts new file mode 100644 index 0000000000..759efc4adc --- /dev/null +++ b/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeDataProvider.ts @@ -0,0 +1,155 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * 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 { TreeNode } from './treeNode'; +import { IControllerTreeChangeHandler } from './controllerTreeChangeHandler'; +import { AddControllerNode } from './addControllerTreeNode'; +import { ControllerRootNode, ControllerNode } from './controllerTreeNode'; +import { IEndPoint } from '../controller/clusterControllerApi'; +import { showErrorMessage } from '../utils'; + +const ConfigNamespace = 'clusterControllers'; +const CredentialNamespace = 'clusterControllerCredentials'; + +export class ControllerTreeDataProvider implements vscode.TreeDataProvider, IControllerTreeChangeHandler { + + private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter(); + public readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event; + private root: ControllerRootNode; + private credentialProvider: azdata.CredentialProvider; + + constructor() { + this.root = new ControllerRootNode(this); + this.loadSavedControllers(); + } + + public async getChildren(element?: TreeNode): Promise { + if (element) { + return element.getChildren(); + } + + if (this.root.hasChildren) { + return this.root.getChildren(); + } else { + return [new AddControllerNode()]; + } + } + + public getTreeItem(element: TreeNode): vscode.TreeItem | Thenable { + return element.getTreeItem(); + } + + public addController( + url: string, + username: string, + password: string, + rememberPassword: boolean, + masterInstance?: IEndPoint + ): void { + this.root.addControllerNode(url, username, password, rememberPassword, masterInstance); + this.notifyNodeChanged(); + } + + public deleteController(url: string, username: string): ControllerNode { + let deleted = this.root.deleteControllerNode(url, username); + if (deleted) { + this.notifyNodeChanged(); + } + return deleted; + } + + public notifyNodeChanged(node?: TreeNode): void { + this._onDidChangeTreeData.fire(node); + } + + public async loadSavedControllers(): Promise { + let config = vscode.workspace.getConfiguration(ConfigNamespace); + if (config && config.controllers) { + let controllers = config.controllers; + this.root.clearChildren(); + for (let c of controllers) { + let password = undefined; + if (c.rememberPassword) { + password = await this.getPassword(c.url, c.username); + } + this.root.addChild(new ControllerNode( + c.url, c.username, password, c.rememberPassword, + undefined, this.root, this, undefined + )); + } + this.notifyNodeChanged(); + } + } + + public async saveControllers(): Promise { + let controllers = this.root.children.map(e => { + let controller = e as ControllerNode; + return { + url: controller.url, + username: controller.username, + password: controller.password, + rememberPassword: !!controller.rememberPassword + }; + }); + + let controllersWithoutPassword = controllers.map(e => { + return { + url: e.url, + username: e.username, + rememberPassword: e.rememberPassword + }; + }); + + try { + await vscode.workspace.getConfiguration(ConfigNamespace).update('controllers', controllersWithoutPassword, true); + } catch (error) { + showErrorMessage(error); + } + + for (let e of controllers) { + if (e.rememberPassword) { + await this.savePassword(e.url, e.username, e.password); + } else { + await this.deletePassword(e.url, e.username); + } + } + } + + private async savePassword(url: string, username: string, password: string): Promise { + let provider = await this.getCredentialProvider(); + let id = this.createId(url, username); + let result = await provider.saveCredential(id, password); + return result; + } + + private async deletePassword(url: string, username: string): Promise { + let provider = await this.getCredentialProvider(); + let id = this.createId(url, username); + let result = await provider.deleteCredential(id); + return result; + } + + private async getPassword(url: string, username: string): Promise { + let provider = await this.getCredentialProvider(); + let id = this.createId(url, username); + let credential = await provider.readCredential(id); + return credential ? credential.password : undefined; + } + + private async getCredentialProvider(): Promise { + if (!this.credentialProvider) { + this.credentialProvider = await azdata.credentials.getProvider(CredentialNamespace); + } + return this.credentialProvider; + } + + private createId(url: string, username: string): string { + return `${url}::${username}`; + } +} diff --git a/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeNode.ts b/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeNode.ts new file mode 100644 index 0000000000..bf76064b1d --- /dev/null +++ b/extensions/big-data-cluster/src/bigDataCluster/tree/controllerTreeNode.ts @@ -0,0 +1,392 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * 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'; +import { IControllerTreeChangeHandler } from './controllerTreeChangeHandler'; +import { TreeNode } from './treeNode'; +import { IconPath, BdcItemType } from '../constants'; +import { IEndPoint, IControllerError, getEndPoints } from '../controller/clusterControllerApi'; +import { showErrorMessage } from '../utils'; + +const localize = nls.loadMessageBundle(); + +export abstract class ControllerTreeNode extends TreeNode { + + constructor( + label: string, + parent: ControllerTreeNode, + private _treeChangeHandler: IControllerTreeChangeHandler, + private _description?: string, + private _nodeType?: string, + private _iconPath?: { dark: string, light: string } + ) { + super(label, parent); + this._description = this._description || this.label; + } + + public async getChildren(): Promise { + return this.children as ControllerTreeNode[]; + } + + public refresh(): void { + super.refresh(); + this.treeChangeHandler.notifyNodeChanged(this); + } + + public getTreeItem(): vscode.TreeItem { + let item: vscode.TreeItem = {}; + item.id = this.id; + item.label = this.label; + item.collapsibleState = this.isLeaf ? + vscode.TreeItemCollapsibleState.None : + vscode.TreeItemCollapsibleState.Collapsed; + item.iconPath = this._iconPath; + item.contextValue = this._nodeType; + item.tooltip = this._description; + item.iconPath = this._iconPath; + return item; + } + + public getNodeInfo(): azdata.NodeInfo { + return { + label: this.label, + isLeaf: this.isLeaf, + errorMessage: undefined, + metadata: undefined, + nodePath: this.nodePath, + nodeStatus: undefined, + nodeType: this._nodeType, + iconType: this._nodeType, + nodeSubType: undefined + }; + } + + public get description(): string { + return this._description; + } + + public set description(description: string) { + this._description = description; + } + + public get nodeType(): string { + return this._nodeType; + } + + public set nodeType(nodeType: string) { + this._nodeType = nodeType; + } + + public set iconPath(iconPath: { dark: string, light: string }) { + this._iconPath = iconPath; + } + + public get iconPath(): { dark: string, light: string } { + return this._iconPath; + } + + public set treeChangeHandler(treeChangeHandler: IControllerTreeChangeHandler) { + this._treeChangeHandler = treeChangeHandler; + } + + public get treeChangeHandler(): IControllerTreeChangeHandler { + return this._treeChangeHandler; + } +} + +export class ControllerRootNode extends ControllerTreeNode { + + private _masterNodeFactory: SqlMasterNodeFactory; + + constructor(treeChangeHandler: IControllerTreeChangeHandler) { + super('root', undefined, treeChangeHandler, undefined, BdcItemType.controllerRoot); + this._masterNodeFactory = new SqlMasterNodeFactory(); + } + + public async getChildren(): Promise { + return this.children as ControllerNode[]; + } + + public addControllerNode(url: string, username: string, password: string, rememberPassword: boolean, masterInstance?: IEndPoint): void { + let controllerNode = this.getExistingControllerNode(url, username); + if (controllerNode) { + controllerNode.password = password; + controllerNode.rememberPassword = rememberPassword; + controllerNode.clearChildren(); + } else { + controllerNode = new ControllerNode(url, username, password, rememberPassword, undefined, this, this.treeChangeHandler, undefined); + this.addChild(controllerNode); + } + + if (masterInstance) { + controllerNode.addSqlMasterNode(masterInstance.endpoint, masterInstance.description); + } + } + + public deleteControllerNode(url: string, username: string): ControllerNode { + if (!url || !username) { + return undefined; + } + let nodes = this.children as ControllerNode[]; + let index = nodes.findIndex(e => e.url === url && e.username === username); + let deleted = undefined; + if (index >= 0) { + deleted = nodes.splice(index, 1); + } + return deleted; + } + + private getExistingControllerNode(url: string, username: string): ControllerNode { + if (!url || !username) { + return undefined; + } + let nodes = this.children as ControllerNode[]; + return nodes.find(e => e.url === url && e.username === username); + } + + public get sqlMasterNodeFactory(): SqlMasterNodeFactory { + return this._masterNodeFactory; + } +} + +export class ControllerNode extends ControllerTreeNode { + + constructor( + private _url: string, + private _username: string, + private _password: string, + private _rememberPassword: boolean, + label: string, + parent: ControllerTreeNode, + treeChangeHandler: IControllerTreeChangeHandler, + description?: string, + ) { + super(label, parent, treeChangeHandler, description, BdcItemType.controller, IconPath.controllerNode); + this.label = label; + this.description = description; + + } + + public async getChildren(): Promise { + if (this.children && this.children.length > 0) { + this.clearChildren(); + } + + if (!this._password) { + vscode.commands.executeCommand('bigDataClusters.command.addController', this); + return this.children as ControllerTreeNode[]; + } + + try { + let response = await getEndPoints(this._url, this._username, this._password, true); + if (response && response.endPoints) { + let master = response.endPoints.find(e => e.name && e.name === 'sql-server-master'); + this.addSqlMasterNode(master.endpoint, master.description); + } + return this.children as ControllerTreeNode[]; + } catch (error) { + showErrorMessage(error); + return this.children as ControllerTreeNode[]; + } + } + + private static toIpAndPort(url: string): string { + if (!url) { + return; + } + return url.trim().replace(/ /g, '').replace(/^.+\:\/\//, '').replace(/:(\d+)$/, ',$1'); + } + + public addSqlMasterNode(endPointAddress: string, description: string): void { + let epFolder = this.getEndPointFolderNode(); + let node = (this.root as ControllerRootNode).sqlMasterNodeFactory + .getSqlMasterNode(endPointAddress, epFolder, undefined, this.treeChangeHandler, description); + epFolder.addChild(node); + } + + private getEndPointFolderNode(): FolderNode { + let label = localize('textSqlServers', 'SQL Servers'); + let epFolderNode = this.children.find(e => e instanceof FolderNode && e.label === label); + if (!epFolderNode) { + epFolderNode = new FolderNode(label, this, this.treeChangeHandler); + this.addChild(epFolderNode); + } + return epFolderNode as FolderNode; + } + + public getTreeItem(): vscode.TreeItem { + let item: vscode.TreeItem = super.getTreeItem(); + item.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed; + return item; + } + + public get url() { + return this._url; + } + + public set url(url: string) { + this._url = url; + } + + public get username() { + return this._username; + } + + public set username(username: string) { + this._username = username; + } + + public get password() { + return this._password; + } + + public set password(pw: string) { + this._password = pw; + } + + public get rememberPassword() { + return this._rememberPassword; + } + + public set rememberPassword(rememberPassword: boolean) { + this._rememberPassword = rememberPassword; + } + + public set label(label: string) { + super.label = label || `controller: ${ControllerNode.toIpAndPort(this._url)} (${this._username})`; + } + + public get label(): string { + return super.label; + } + + public set description(description: string) { + super.description = description || super.label; + } + + public get description(): string { + return super.description; + } +} + +export class FolderNode extends ControllerTreeNode { + constructor( + label: string, + parent: ControllerTreeNode, + treeChangeHandler: IControllerTreeChangeHandler + ) { + super(label, parent, treeChangeHandler, label, BdcItemType.folder, IconPath.folderNode); + } +} + +export class SqlMasterNode extends ControllerTreeNode { + private static readonly _role: string = 'sql-server-master'; + private _username: string; + private _password: string; + + constructor( + private _endPointAddress: string, + parent: ControllerTreeNode, + label: string, + treeChangeHandler: IControllerTreeChangeHandler, + description?: string, + ) { + super(label, parent, treeChangeHandler, description, BdcItemType.sqlMaster, IconPath.sqlMasterNode); + this._username = 'sa'; + this.label = label; + this.description = description; + } + + private getControllerPassword(): string { + if (!this._password) { + let current: TreeNode = this; + while (current && !(current instanceof ControllerNode)) { + current = current.parent; + } + this._password = current && current instanceof ControllerNode ? current.password : undefined; + } + return this._password; + } + + public getTreeItem(): vscode.TreeItem { + let item = super.getTreeItem(); + let connectionProfile: azdata.IConnectionProfile = { + id: this.id, + connectionName: this.id, + serverName: this._endPointAddress, + databaseName: '', + userName: this._username, + password: this.getControllerPassword(), + authenticationType: 'SqlLogin', + savePassword: false, + groupFullName: '', + groupId: '', + providerName: 'MSSQL', + saveProfile: false, + options: {} + }; + return Object.assign(item, { payload: connectionProfile, childProvider: 'MSSQL' }); + } + + public get role() { + return SqlMasterNode._role; + } + + public get endPointAddress() { + return this._endPointAddress; + } + + public set endPointAddress(endPointAddress: string) { + this._endPointAddress = endPointAddress; + } + + public set label(label: string) { + super.label = label || `master: ${this._endPointAddress} (${this._username})`; + } + + public get label(): string { + return super.label; + } + + public set description(description: string) { + super.description = description || super.label; + } + + public get description(): string { + return super.description; + } +} + +export class SqlMasterNodeFactory { + private registry: {} = {}; + + public getSqlMasterNode( + endPointAddress: string, + parent: ControllerTreeNode, + label: string, + treeChangeHandler: IControllerTreeChangeHandler, + description?: string + ): SqlMasterNode { + let id = this.createRegistryId(endPointAddress, 'sa'); + if (!this.registry[id]) { + this.registry[id] = new SqlMasterNode(endPointAddress, parent, label, treeChangeHandler, description); + } else { + let node = this.registry[id] as SqlMasterNode; + node.parent = parent; + node.label = label; + node.treeChangeHandler = treeChangeHandler; + description = description; + } + return this.registry[id] as SqlMasterNode; + } + + private createRegistryId(endPointAddress: string, username: string): string { + return `${endPointAddress}::${username}`; + } +} diff --git a/extensions/big-data-cluster/src/bigDataCluster/tree/treeNode.ts b/extensions/big-data-cluster/src/bigDataCluster/tree/treeNode.ts new file mode 100644 index 0000000000..fa6177e32b --- /dev/null +++ b/extensions/big-data-cluster/src/bigDataCluster/tree/treeNode.ts @@ -0,0 +1,195 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * 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 { generateGuid } from '../utils'; + +export abstract class TreeNode { + + private _id: string; + private _children: TreeNode[]; + private _isLeaf: boolean; + + constructor(private _label: string, private _parent?: TreeNode) { + this.resetId(); + } + + public resetId(): void { + this._id = (this._label || '_') + `::${generateGuid()}`; + } + + public get id(): string { + return this._id; + } + + public set label(label: string) { + if (!this._label) { + this._label = label; + this.resetId(); + } else { + this._label = label; + } + } + + public get label(): string { + return this._label; + } + + public set parent(parent: TreeNode) { + this._parent = parent; + } + + public get parent(): TreeNode { + return this._parent; + } + + public get children(): TreeNode[] { + if (!this._children) { + this._children = []; + } + return this._children; + } + + public get hasChildren(): boolean { + return this.children && this.children.length > 0; + } + + public set isLeaf(isLeaf: boolean) { + this._isLeaf = isLeaf; + } + + public get isLeaf(): boolean { + return this._isLeaf; + } + + public get root(): TreeNode { + return TreeNode.getRoot(this); + } + + public equals(node: TreeNode): boolean { + if (!node) { + return undefined; + } + return this.nodePath === node.nodePath; + } + + public refresh(): void { + this.resetId(); + } + + public static getRoot(node: TreeNode): TreeNode { + if (!node) { + return undefined; + } + let current: TreeNode = node; + while (current.parent) { + current = current.parent; + } + return current; + } + + public get nodePath(): string { + return TreeNode.getNodePath(this); + } + + public static getNodePath(node: TreeNode): string { + if (!node) { + return undefined; + } + + let current: TreeNode = node; + let path = current._id; + while (current.parent) { + current = current.parent; + path = `${current._id}/${path}`; + } + return path; + } + + public async findNode(condition: (node: TreeNode) => boolean, expandIfNeeded?: boolean): Promise { + return TreeNode.findNode(this, condition, expandIfNeeded); + } + + public static async findNode(node: TreeNode, condition: (node: TreeNode) => boolean, expandIfNeeded?: boolean): Promise { + if (!node || !condition) { + return undefined; + } + let result: TreeNode = undefined; + let nodesToCheck: TreeNode[] = [node]; + while (nodesToCheck.length > 0) { + let current = nodesToCheck.shift(); + if (condition(current)) { + result = current; + break; + } + if (current.hasChildren) { + nodesToCheck = nodesToCheck.concat(current.children); + } else if (expandIfNeeded) { + let children = await current.getChildren(); + if (children && children.length > 0) { + nodesToCheck = nodesToCheck.concat(children); + } + } + } + return result; + } + + public async filterNode(condition: (node: TreeNode) => boolean, expandIfNeeded?: boolean): Promise { + return TreeNode.filterNode(this, condition, expandIfNeeded); + } + + public static async filterNode(node: TreeNode, condition: (node: TreeNode) => boolean, expandIfNeeded?: boolean): Promise { + if (!node || !condition) { + return undefined; + } + let result: TreeNode[] = []; + let nodesToCheck: TreeNode[] = [node]; + while (nodesToCheck.length > 0) { + let current = nodesToCheck.shift(); + if (condition(current)) { + result.push(current); + } + if (current.hasChildren) { + nodesToCheck = nodesToCheck.concat(current.children); + } else if (expandIfNeeded) { + let children = await current.getChildren(); + if (children && children.length > 0) { + nodesToCheck = nodesToCheck.concat(children); + } + } + } + return result; + } + + public async findNodeByPath(path: string, expandIfNeeded?: boolean): Promise { + return TreeNode.findNodeByPath(this, path, expandIfNeeded); + } + + public static async findNodeByPath(node: TreeNode, path: string, expandIfNeeded?: boolean): Promise { + return TreeNode.findNode(node, node => { + return node.nodePath && (node.nodePath === path || node.nodePath.startsWith(path)); + }, expandIfNeeded); + } + + public addChild(node: TreeNode): void { + if (!this._children) { + this._children = []; + } + this._children.push(node); + } + + public clearChildren(): void { + if (this._children) { + this._children = []; + } + } + + public abstract async getChildren(): Promise; + public abstract getTreeItem(): vscode.TreeItem; + public abstract getNodeInfo(): azdata.NodeInfo; +} diff --git a/extensions/big-data-cluster/src/bigDataCluster/utils.ts b/extensions/big-data-cluster/src/bigDataCluster/utils.ts new file mode 100644 index 0000000000..2c7f3acdda --- /dev/null +++ b/extensions/big-data-cluster/src/bigDataCluster/utils.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as vscode from 'vscode'; + +export function generateGuid(): string { + let hexValues: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']; + let oct: string = ''; + let tmp: number; + for (let a: number = 0; a < 4; a++) { + tmp = (4294967296 * Math.random()) | 0; + oct += hexValues[tmp & 0xF] + + hexValues[tmp >> 4 & 0xF] + + hexValues[tmp >> 8 & 0xF] + + hexValues[tmp >> 12 & 0xF] + + hexValues[tmp >> 16 & 0xF] + + hexValues[tmp >> 20 & 0xF] + + hexValues[tmp >> 24 & 0xF] + + hexValues[tmp >> 28 & 0xF]; + } + let clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0]; + return oct.substr(0, 8) + '-' + oct.substr(9, 4) + '-4' + oct.substr(13, 3) + '-' + clockSequenceHi + oct.substr(16, 3) + '-' + oct.substr(19, 12); +} + +export function showErrorMessage(error: any): void { + if (error) { + let text: string = undefined; + if (typeof error === 'string') { + text = error as string; + } else if (typeof error === 'object' && error !== null) { + let message = error.message; + let code = error.code || error.errno; + text = (code ? `${code} ` : '') + message; + } else { + text = `${error}`; + } + vscode.window.showErrorMessage(text); + } +} diff --git a/extensions/big-data-cluster/src/config/config.ts b/extensions/big-data-cluster/src/config/config.ts deleted file mode 100644 index 05e440ced8..0000000000 --- a/extensions/big-data-cluster/src/config/config.ts +++ /dev/null @@ -1,121 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { Host } from '../kubectl/host'; -import { Shell, Platform } from '../utility/shell'; - -const EXTENSION_CONFIG_KEY = 'mssql-bdc'; -const KUBECONFIG_PATH_KEY = 'mssql-bdc.kubeconfig'; -const KNOWN_KUBECONFIGS_KEY = 'mssql-bdc.knownKubeconfigs'; - -export async function addPathToConfig(configKey: string, value: string): Promise { - await setConfigValue(configKey, value); -} - -async function setConfigValue(configKey: string, value: any): Promise { - await atAllConfigScopes(addValueToConfigAtScope, configKey, value); -} - -async function addValueToConfigAtScope(configKey: string, value: any, scope: vscode.ConfigurationTarget, valueAtScope: any, createIfNotExist: boolean): Promise { - if (!createIfNotExist) { - if (!valueAtScope || !(valueAtScope[configKey])) { - return; - } - } - - let newValue: any = {}; - if (valueAtScope) { - newValue = Object.assign({}, valueAtScope); - } - newValue[configKey] = value; - await vscode.workspace.getConfiguration().update(EXTENSION_CONFIG_KEY, newValue, scope); -} - -async function addValueToConfigArray(configKey: string, value: string): Promise { - await atAllConfigScopes(addValueToConfigArrayAtScope, configKey, value); -} - -async function addValueToConfigArrayAtScope(configKey: string, value: string, scope: vscode.ConfigurationTarget, valueAtScope: any, createIfNotExist: boolean): Promise { - if (!createIfNotExist) { - if (!valueAtScope || !(valueAtScope[configKey])) { - return; - } - } - - let newValue: any = {}; - if (valueAtScope) { - newValue = Object.assign({}, valueAtScope); - } - const arrayEntry: string[] = newValue[configKey] || []; - arrayEntry.push(value); - newValue[configKey] = arrayEntry; - await vscode.workspace.getConfiguration().update(EXTENSION_CONFIG_KEY, newValue, scope); -} - -type ConfigUpdater = (configKey: string, value: T, scope: vscode.ConfigurationTarget, valueAtScope: any, createIfNotExist: boolean) => Promise; - -async function atAllConfigScopes(fn: ConfigUpdater, configKey: string, value: T): Promise { - const config = vscode.workspace.getConfiguration().inspect(EXTENSION_CONFIG_KEY)!; - await fn(configKey, value, vscode.ConfigurationTarget.Global, config.globalValue, true); - await fn(configKey, value, vscode.ConfigurationTarget.Workspace, config.workspaceValue, false); - await fn(configKey, value, vscode.ConfigurationTarget.WorkspaceFolder, config.workspaceFolderValue, false); -} - -// Functions for working with the list of known kubeconfigs - -export function getKnownKubeconfigs(): string[] { - const kkcConfig = vscode.workspace.getConfiguration(EXTENSION_CONFIG_KEY)[KNOWN_KUBECONFIGS_KEY]; - if (!kkcConfig || !kkcConfig.length) { - return []; - } - return kkcConfig as string[]; -} - -export async function addKnownKubeconfig(kubeconfigPath: string) { - await addValueToConfigArray(KNOWN_KUBECONFIGS_KEY, kubeconfigPath); -} - -// Functions for working with the active kubeconfig setting - -export async function setActiveKubeconfig(kubeconfig: string): Promise { - await addPathToConfig(KUBECONFIG_PATH_KEY, kubeconfig); -} - -export function getActiveKubeconfig(): string { - return vscode.workspace.getConfiguration(EXTENSION_CONFIG_KEY)[KUBECONFIG_PATH_KEY]; -} - -// Functions for working with tool paths - -export function getToolPath(host: Host, shell: Shell, tool: string): string | undefined { - const baseKey = toolPathBaseKey(tool); - return getPathSetting(host, shell, baseKey); -} - -function getPathSetting(host: Host, shell: Shell, baseKey: string): string | undefined { - const os = shell.platform(); - const osOverridePath = host.getConfiguration(EXTENSION_CONFIG_KEY)[osOverrideKey(os, baseKey)]; - return osOverridePath || host.getConfiguration(EXTENSION_CONFIG_KEY)[baseKey]; -} - -export function toolPathBaseKey(tool: string): string { - return `mssql-bdc.${tool}-path`; -} - -function osOverrideKey(os: Platform, baseKey: string): string { - const osKey = osKeyString(os); - return osKey ? `${baseKey}.${osKey}` : baseKey; // The 'else' clause should never happen so don't worry that this would result in double-checking a missing base key -} - -function osKeyString(os: Platform): string | null { - switch (os) { - case Platform.Windows: return 'windows'; - case Platform.MacOS: return 'mac'; - case Platform.Linux: return 'linux'; - default: return null; - } -} - diff --git a/extensions/big-data-cluster/src/data/kubeConfigParser.ts b/extensions/big-data-cluster/src/data/kubeConfigParser.ts deleted file mode 100644 index 022b1d981a..0000000000 --- a/extensions/big-data-cluster/src/data/kubeConfigParser.ts +++ /dev/null @@ -1,35 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import { ClusterInfo } from '../interfaces'; - -export interface IKubeConfigParser { - parse(configPath: string): ClusterInfo[]; -} - -export class TestKubeConfigParser implements IKubeConfigParser { - parse(configPath: string): ClusterInfo[] { - let clusters = []; - for (let i = 0; i < 18; i++) { - let name; - if (i % 2 === 0) { - name = `kubernetes cluster ${i}`; - } - else { - name = 'cluster dev ' + i; - } - clusters.push( - { - displayName: name, - name: `kub-dev-xxxx-cluster-${i}`, - user: 'root' - } - ); - } - return clusters; - } -} - diff --git a/extensions/big-data-cluster/src/extension.ts b/extensions/big-data-cluster/src/extension.ts new file mode 100644 index 0000000000..a5b937db2d --- /dev/null +++ b/extensions/big-data-cluster/src/extension.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import { ControllerTreeDataProvider } from './bigDataCluster/tree/controllerTreeDataProvider'; +import { IconPath } from './bigDataCluster/constants'; +import { TreeNode } from './bigDataCluster/tree/treeNode'; +import { AddControllerDialogModel, AddControllerDialog } from './bigDataCluster/dialog/addControllerDialog'; +import { ControllerNode } from './bigDataCluster/tree/controllerTreeNode'; + +const localize = nls.loadMessageBundle(); + +export function activate(extensionContext: vscode.ExtensionContext) { + IconPath.setExtensionContext(extensionContext); + let treeDataProvider = new ControllerTreeDataProvider(); + + registerTreeDataProvider(treeDataProvider); + registerCommands(treeDataProvider); +} + +export function deactivate() { +} + +function registerTreeDataProvider(treeDataProvider: ControllerTreeDataProvider): void { + vscode.window.registerTreeDataProvider('sqlBigDataCluster', treeDataProvider); +} + +function registerCommands(treeDataProvider: ControllerTreeDataProvider): void { + vscode.commands.registerCommand('bigDataClusters.command.addController', (node?: TreeNode) => { + addBdcController(treeDataProvider, node); + }); + + vscode.commands.registerCommand('bigDataClusters.command.deleteController', (node: TreeNode) => { + deleteBdcController(treeDataProvider, node); + }); + + vscode.commands.registerCommand('bigDataClusters.command.refreshController', (node: TreeNode) => { + if (!node) { + return; + } + treeDataProvider.notifyNodeChanged(node); + }); +} + +function addBdcController(treeDataProvider: ControllerTreeDataProvider, node?: TreeNode): void { + let model = new AddControllerDialogModel(treeDataProvider, node); + let dialog = new AddControllerDialog(model); + dialog.showDialog(); +} + +async function deleteBdcController(treeDataProvider: ControllerTreeDataProvider, node: TreeNode): Promise { + if (!node && !(node instanceof ControllerNode)) { + return; + } + + let controllerNode = node as ControllerNode; + + let choices: { [id: string]: boolean } = {}; + choices[localize('textYes', 'Yes')] = true; + choices[localize('textNo', 'No')] = false; + + let options = { + ignoreFocusOut: false, + placeHolder: localize('textConfirmDeleteController', 'Are you sure you want to delete \'{0}\'?', controllerNode.label) + }; + + let result = await vscode.window.showQuickPick(Object.keys(choices), options); + let remove: boolean = !!(result && choices[result]); + if (remove) { + deleteControllerInternal(treeDataProvider, controllerNode); + } + return remove; +} + +function deleteControllerInternal(treeDataProvider: ControllerTreeDataProvider, controllerNode: ControllerNode): void { + let deleted = treeDataProvider.deleteController(controllerNode.url, controllerNode.username); + if (deleted) { + treeDataProvider.saveControllers(); + } +} diff --git a/extensions/big-data-cluster/src/installer/download.ts b/extensions/big-data-cluster/src/installer/download.ts deleted file mode 100644 index f80aba6827..0000000000 --- a/extensions/big-data-cluster/src/installer/download.ts +++ /dev/null @@ -1,45 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as path from 'path'; -import * as stream from 'stream'; -import * as tmp from 'tmp'; - -import { succeeded, Errorable } from '../interfaces'; - -type DownloadFunc = - (url: string, destination?: string, options?: any) - => Promise & stream.Duplex; // Stream has additional events - see https://www.npmjs.com/package/download - -let download: DownloadFunc; - -function ensureDownloadFunc() { - if (!download) { - const home = process.env['HOME']; - download = require('download'); - if (home) { - process.env['HOME'] = home; - } - } -} - -export async function toTempFile(sourceUrl: string): Promise> { - const tempFileObj = tmp.fileSync({ prefix: 'mssql-bdc-autoinstall-' }); - const downloadResult = await to(sourceUrl, tempFileObj.name); - if (succeeded(downloadResult)) { - return { succeeded: true, result: tempFileObj.name }; - } - return { succeeded: false, error: downloadResult.error }; -} - -export async function to(sourceUrl: string, destinationFile: string): Promise> { - ensureDownloadFunc(); - try { - await download(sourceUrl, path.dirname(destinationFile), { filename: path.basename(destinationFile) }); - return { succeeded: true, result: null }; - } catch (e) { - return { succeeded: false, error: [e.message] }; - } -} diff --git a/extensions/big-data-cluster/src/installer/installer.ts b/extensions/big-data-cluster/src/installer/installer.ts deleted file mode 100644 index 62b0ad37c9..0000000000 --- a/extensions/big-data-cluster/src/installer/installer.ts +++ /dev/null @@ -1,72 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as download from './download'; -import * as fs from 'fs'; -import mkdirp = require('mkdirp'); -import * as path from 'path'; -import * as nls from 'vscode-nls'; -const localize = nls.loadMessageBundle(); - -import { Shell, Platform } from '../utility/shell'; -import { Errorable, failed } from '../interfaces'; -import { addPathToConfig, toolPathBaseKey } from '../config/config'; - -export async function installKubectl(shell: Shell): Promise> { - const tool = 'kubectl'; - const binFile = (shell.isUnix()) ? 'kubectl' : 'kubectl.exe'; - const os = platformUrlString(shell.platform()); - - const version = await getStableKubectlVersion(); - if (failed(version)) { - return { succeeded: false, error: version.error }; - } - - const installFolder = getInstallFolder(shell, tool); - mkdirp.sync(installFolder); - - const kubectlUrl = `https://storage.googleapis.com/kubernetes-release/release/${version.result.trim()}/bin/${os}/amd64/${binFile}`; - const downloadFile = path.join(installFolder, binFile); - const downloadResult = await download.to(kubectlUrl, downloadFile); - if (failed(downloadResult)) { - return { succeeded: false, error: [localize('downloadKubectlFailed', 'Failed to download kubectl: {0}', downloadResult.error[0])] }; - } - - if (shell.isUnix()) { - fs.chmodSync(downloadFile, '0777'); - } - - await addPathToConfig(toolPathBaseKey(tool), downloadFile); - return { succeeded: true, result: null }; -} - -async function getStableKubectlVersion(): Promise> { - const downloadResult = await download.toTempFile('https://storage.googleapis.com/kubernetes-release/release/stable.txt'); - if (failed(downloadResult)) { - return { succeeded: false, error: [localize('kubectlVersionCheckFailed', 'Failed to establish kubectl stable version: {0}', downloadResult.error[0])] }; - } - const version = fs.readFileSync(downloadResult.result, 'utf-8'); - fs.unlinkSync(downloadResult.result); - return { succeeded: true, result: version }; -} - -export function getInstallFolder(shell: Shell, tool: string): string { - return path.join(shell.home(), `.mssql-bdc/tools/${tool}`); -} - -function platformUrlString(platform: Platform, supported?: Platform[]): string | null { - if (supported && supported.indexOf(platform) < 0) { - return null; - } - switch (platform) { - case Platform.Windows: return 'windows'; - case Platform.MacOS: return 'darwin'; - case Platform.Linux: return 'linux'; - default: return null; - } -} - - diff --git a/extensions/big-data-cluster/src/interfaces.ts b/extensions/big-data-cluster/src/interfaces.ts deleted file mode 100644 index a4f9657374..0000000000 --- a/extensions/big-data-cluster/src/interfaces.ts +++ /dev/null @@ -1,124 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -export interface ClusterInfo { - name: string; - displayName: string; - user: string; -} - -export enum TargetClusterType { - ExistingKubernetesCluster, - NewAksCluster -} - -export interface Succeeded { - readonly succeeded: true; - readonly result: T; -} - -export interface Failed { - readonly succeeded: false; - readonly error: string[]; -} - -export type Errorable = Succeeded | Failed; - -export function succeeded(e: Errorable): e is Succeeded { - return e.succeeded; -} - -export function failed(e: Errorable): e is Failed { - return !e.succeeded; -} -export interface ClusterPorts { - sql: string; - knox: string; - controller: string; - proxy: string; - grafana: string; - kibana: string; -} - -export interface ContainerRegistryInfo { - registry: string; - repository: string; - imageTag: string; -} - -export interface TargetClusterTypeInfo { - enabled: boolean; - type: TargetClusterType; - name: string; - fullName: string; - description: string; - iconPath: { - dark: string, - light: string - }; -} - -export interface ToolInfo { - name: string; - description: string; - version: string; - status: ToolInstallationStatus; -} - -export enum ToolInstallationStatus { - Installed, - NotInstalled, - Installing, - FailedToInstall -} - -export enum ClusterType { - Unknown = 0, - AKS, - Minikube, - Kubernetes, - Other -} - -export interface ClusterProfile { - name: string; - sqlServerMasterConfiguration: SQLServerMasterConfiguration; - computePoolConfiguration: PoolConfiguration; - dataPoolConfiguration: PoolConfiguration; - storagePoolConfiguration: PoolConfiguration; - sparkPoolConfiguration: PoolConfiguration; -} - -export interface PoolConfiguration { - type: ClusterPoolType; - scale: number; - maxScale?: number; - hardwareLabel?: string; -} - -export interface SQLServerMasterConfiguration extends PoolConfiguration { - engineOnly: boolean; -} - -export enum ClusterPoolType { - SQL, - Compute, - Data, - Storage, - Spark -} - -export interface ClusterResourceSummary { - hardwareLabels: HardwareLabel[]; -} - -export interface HardwareLabel { - name: string; - totalNodes: number; - totalCores: number; - totalMemoryInGB: number; - totalDisks: number; -} \ No newline at end of file diff --git a/extensions/big-data-cluster/src/kubectl/binutil.ts b/extensions/big-data-cluster/src/kubectl/binutil.ts deleted file mode 100644 index 85d1195ed2..0000000000 --- a/extensions/big-data-cluster/src/kubectl/binutil.ts +++ /dev/null @@ -1,121 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vscode-nls'; -const localize = nls.loadMessageBundle(); - -import { Shell } from '../utility/shell'; -import { Host } from './host'; -import { FS } from '../utility/fs'; - -export interface BinCheckContext { - readonly host: Host; - readonly fs: FS; - readonly shell: Shell; - readonly installDependenciesCallback: () => void; - binFound: boolean; - binPath: string; -} - -interface FindBinaryResult { - err: number | null; - output: string; -} - -async function findBinary(shell: Shell, binName: string): Promise { - let cmd = `which ${binName}`; - - if (shell.isWindows()) { - cmd = `where.exe ${binName}.exe`; - } - - const opts = { - async: true, - env: { - HOME: process.env.HOME, - PATH: process.env.PATH - } - }; - - const execResult = await shell.execCore(cmd, opts); - if (execResult.code) { - return { err: execResult.code, output: execResult.stderr }; - } - - return { err: null, output: execResult.stdout }; -} - -export function execPath(shell: Shell, basePath: string): string { - let bin = basePath; - if (shell.isWindows() && bin && !(bin.endsWith('.exe'))) { - bin = bin + '.exe'; - } - return bin; -} - -type CheckPresentFailureReason = 'inferFailed' | 'configuredFileMissing'; -const installDependenciesAction = localize('installDependenciesAction', 'Install dependencies'); -const learnMoreAction = localize('learnMoreAction', 'Learn more'); -function alertNoBin(host: Host, binName: string, failureReason: CheckPresentFailureReason, message: string, installDependencies: () => void): void { - switch (failureReason) { - case 'inferFailed': - host.showErrorMessage(message, installDependenciesAction, learnMoreAction).then( - (str) => { - switch (str) { - case learnMoreAction: - host.showInformationMessage(localize('moreInfoMsg', 'Add {0} directory to path, or set "mssql-bdc.{0}-path" config to {0} binary.', binName)); - break; - case installDependenciesAction: - installDependencies(); - break; - } - - } - ); - break; - case 'configuredFileMissing': - host.showErrorMessage(message, installDependenciesAction).then( - (str) => { - if (str === installDependenciesAction) { - installDependencies(); - } - } - ); - break; - } -} - -export async function checkForBinary(context: BinCheckContext, bin: string | undefined, binName: string, inferFailedMessage: string, configuredFileMissingMessage: string, alertOnFail: boolean): Promise { - if (!bin) { - const fb = await findBinary(context.shell, binName); - - if (fb.err || fb.output.length === 0) { - if (alertOnFail) { - alertNoBin(context.host, binName, 'inferFailed', inferFailedMessage, context.installDependenciesCallback); - } - return false; - } - - context.binFound = true; - - return true; - } - - if (context.shell.isWindows) { - context.binFound = context.fs.existsSync(bin); - } else { - const sr = await context.shell.exec(`ls ${bin}`); - context.binFound = (!!sr && sr.code === 0); - } - if (context.binFound) { - context.binPath = bin; - } else { - if (alertOnFail) { - alertNoBin(context.host, binName, 'configuredFileMissing', configuredFileMissingMessage, context.installDependenciesCallback); - } - } - - return context.binFound; -} diff --git a/extensions/big-data-cluster/src/kubectl/compatibility.ts b/extensions/big-data-cluster/src/kubectl/compatibility.ts deleted file mode 100644 index 8cea1dba11..0000000000 --- a/extensions/big-data-cluster/src/kubectl/compatibility.ts +++ /dev/null @@ -1,66 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Errorable, failed } from '../interfaces'; - -interface CompatibilityGuaranteed { - readonly guaranteed: true; -} - -interface CompatibilityNotGuaranteed { - readonly guaranteed: false; - readonly didCheck: boolean; - readonly clientVersion: string; - readonly serverVersion: string; -} - -export type Compatibility = CompatibilityGuaranteed | CompatibilityNotGuaranteed; - -export function isGuaranteedCompatible(c: Compatibility): c is CompatibilityGuaranteed { - return c.guaranteed; -} - -export interface Version { - readonly major: string; - readonly minor: string; - readonly gitVersion: string; -} - -export async function check(kubectlLoadJSON: (cmd: string) => Promise>): Promise { - const version = await kubectlLoadJSON('version -o json'); - if (failed(version)) { - return { - guaranteed: false, - didCheck: false, - clientVersion: '', - serverVersion: '' - }; - } - - const clientVersion: Version = version.result.clientVersion; - const serverVersion: Version = version.result.serverVersion; - - if (isCompatible(clientVersion, serverVersion)) { - return { guaranteed: true }; - } - - return { - guaranteed: false, - didCheck: true, - clientVersion: clientVersion.gitVersion, - serverVersion: serverVersion.gitVersion - }; -} - -function isCompatible(clientVersion: Version, serverVersion: Version): boolean { - if (clientVersion.major === serverVersion.major) { - const clientMinor = Number.parseInt(clientVersion.minor); - const serverMinor = Number.parseInt(serverVersion.minor); - if (Number.isInteger(clientMinor) && Number.isInteger(serverMinor) && Math.abs(clientMinor - serverMinor) <= 1) { - return true; - } - } - return false; -} diff --git a/extensions/big-data-cluster/src/kubectl/host.ts b/extensions/big-data-cluster/src/kubectl/host.ts deleted file mode 100644 index de9eb5fbde..0000000000 --- a/extensions/big-data-cluster/src/kubectl/host.ts +++ /dev/null @@ -1,42 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; - -export interface Host { - showErrorMessage(message: string, ...items: string[]): Thenable; - showWarningMessage(message: string, ...items: string[]): Thenable; - showInformationMessage(message: string, ...items: string[]): Thenable; - getConfiguration(key: string): any; - onDidChangeConfiguration(listener: (ch: vscode.ConfigurationChangeEvent) => any): vscode.Disposable; -} - -export const host: Host = { - showErrorMessage: showErrorMessage, - showWarningMessage: showWarningMessage, - showInformationMessage: showInformationMessage, - getConfiguration: getConfiguration, - onDidChangeConfiguration: onDidChangeConfiguration, -}; - -function showErrorMessage(message: string, ...items: string[]): Thenable { - return vscode.window.showErrorMessage(message, ...items); -} - -function showWarningMessage(message: string, ...items: string[]): Thenable { - return vscode.window.showWarningMessage(message, ...items); -} - -function showInformationMessage(message: string, ...items: string[]): Thenable { - return vscode.window.showInformationMessage(message, ...items); -} - -function getConfiguration(key: string): any { - return vscode.workspace.getConfiguration(key); -} - -function onDidChangeConfiguration(listener: (e: vscode.ConfigurationChangeEvent) => any): vscode.Disposable { - return vscode.workspace.onDidChangeConfiguration(listener); -} diff --git a/extensions/big-data-cluster/src/kubectl/kubectl.ts b/extensions/big-data-cluster/src/kubectl/kubectl.ts deleted file mode 100644 index 26d793d851..0000000000 --- a/extensions/big-data-cluster/src/kubectl/kubectl.ts +++ /dev/null @@ -1,142 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vscode-nls'; -const localize = nls.loadMessageBundle(); - -import { Host } from './host'; -import { FS } from '../utility/fs'; -import { Shell, ShellResult } from '../utility/shell'; -import * as binutil from './binutil'; -import { Errorable } from '../interfaces'; -import * as compatibility from './compatibility'; -import { getToolPath } from '../config/config'; - -export interface Kubectl { - checkPresent(errorMessageMode: CheckPresentMessageMode): Promise; - asJson(command: string): Promise>; - invokeAsync(command: string, stdin?: string): Promise; - getContext(): Context; -} - -interface Context { - readonly host: Host; - readonly fs: FS; - readonly shell: Shell; - readonly installDependenciesCallback: () => void; - binFound: boolean; - binPath: string; -} - -class KubectlImpl implements Kubectl { - constructor(host: Host, fs: FS, shell: Shell, installDependenciesCallback: () => void, kubectlFound: boolean) { - this.context = { host: host, fs: fs, shell: shell, installDependenciesCallback: installDependenciesCallback, binFound: kubectlFound, binPath: 'kubectl' }; - } - - readonly context: Context; - - checkPresent(errorMessageMode: CheckPresentMessageMode): Promise { - return checkPresent(this.context, errorMessageMode); - } - asJson(command: string): Promise> { - return asJson(this.context, command); - } - invokeAsync(command: string, stdin?: string): Promise { - return invokeAsync(this.context, command, stdin); - } - - getContext(): Context { - return this.context; - } -} - -export function create(host: Host, fs: FS, shell: Shell, installDependenciesCallback: () => void): Kubectl { - return new KubectlImpl(host, fs, shell, installDependenciesCallback, false); -} - -export enum CheckPresentMessageMode { - Command, - Activation, - Silent, -} - -async function checkPresent(context: Context, errorMessageMode: CheckPresentMessageMode): Promise { - if (context.binFound) { - return true; - } - - return await checkForKubectlInternal(context, errorMessageMode); -} - -async function checkForKubectlInternal(context: Context, errorMessageMode: CheckPresentMessageMode): Promise { - const binName = 'kubectl'; - const bin = getToolPath(context.host, context.shell, binName); - - const contextMessage = getCheckKubectlContextMessage(errorMessageMode); - const inferFailedMessage = localize('binaryNotFound', 'Could not find {0} binary. {1}', binName, contextMessage); - const configuredFileMissingMessage = localize('binaryNotInstalled', '{0} is not installed. {1}', bin, contextMessage); - - return await binutil.checkForBinary(context, bin, binName, inferFailedMessage, configuredFileMissingMessage, errorMessageMode !== CheckPresentMessageMode.Silent); -} - -function getCheckKubectlContextMessage(errorMessageMode: CheckPresentMessageMode): string { - if (errorMessageMode === CheckPresentMessageMode.Activation) { - return localize('kubernetesRequired', ' SQL Server Big data cluster requires kubernetes.'); - } else if (errorMessageMode === CheckPresentMessageMode.Command) { - return localize('cannotExecuteCmd', ' Cannot execute command.'); - } - return ''; -} - -async function invokeAsync(context: Context, command: string, stdin?: string): Promise { - if (await checkPresent(context, CheckPresentMessageMode.Command)) { - const bin = baseKubectlPath(context); - const cmd = `${bin} ${command}`; - const sr = await context.shell.exec(cmd, stdin); - if (sr && sr.code !== 0) { - checkPossibleIncompatibility(context); - } - return sr; - } else { - return { code: -1, stdout: '', stderr: '' }; - } -} - -// TODO: invalidate this when the context changes or if we know kubectl has changed (e.g. config) -let checkedCompatibility = false; // We don't want to spam the user (or CPU!) repeatedly running the version check - -async function checkPossibleIncompatibility(context: Context): Promise { - if (checkedCompatibility) { - return; - } - checkedCompatibility = true; - const compat = await compatibility.check((cmd) => asJson(context, cmd)); - if (!compatibility.isGuaranteedCompatible(compat) && compat.didCheck) { - const versionAlert = localize('kubectlVersionIncompatible', 'kubectl version ${0} may be incompatible with cluster Kubernetes version {1}', compat.clientVersion, compat.serverVersion); - context.host.showWarningMessage(versionAlert); - } -} - - -export function baseKubectlPath(context: Context): string { - let bin = getToolPath(context.host, context.shell, 'kubectl'); - if (!bin) { - bin = 'kubectl'; - } - return bin; -} - -async function asJson(context: Context, command: string): Promise> { - const shellResult = await invokeAsync(context, command); - if (!shellResult) { - return { succeeded: false, error: [localize('cannotRunCommand', 'Unable to run command ({0})', command)] }; - } - - if (shellResult.code === 0) { - return { succeeded: true, result: JSON.parse(shellResult.stdout.trim()) as T }; - - } - return { succeeded: false, error: [shellResult.stderr] }; -} \ No newline at end of file diff --git a/extensions/big-data-cluster/src/kubectl/kubectlUtils.ts b/extensions/big-data-cluster/src/kubectl/kubectlUtils.ts deleted file mode 100644 index c2bf441860..0000000000 --- a/extensions/big-data-cluster/src/kubectl/kubectlUtils.ts +++ /dev/null @@ -1,136 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -const localize = nls.loadMessageBundle(); - -import { Kubectl } from './kubectl'; -import { failed, ClusterType } from '../interfaces'; - -export interface KubectlContext { - readonly clusterName: string; - readonly contextName: string; - readonly userName: string; - readonly active: boolean; -} - -interface Kubeconfig { - readonly apiVersion: string; - readonly 'current-context': string; - readonly clusters: { - readonly name: string; - readonly cluster: { - readonly server: string; - readonly 'certificate-authority'?: string; - readonly 'certificate-authority-data'?: string; - }; - }[] | undefined; - readonly contexts: { - readonly name: string; - readonly context: { - readonly cluster: string; - readonly user: string; - readonly namespace?: string; - }; - }[] | undefined; - readonly users: { - readonly name: string; - readonly user: {}; - }[] | undefined; -} - -export interface ClusterConfig { - readonly server: string; - readonly certificateAuthority: string | undefined; -} - - - -async function getKubeconfig(kubectl: Kubectl): Promise { - const shellResult = await kubectl.asJson('config view -o json'); - if (failed(shellResult)) { - vscode.window.showErrorMessage(shellResult.error[0]); - return null; - } - return shellResult.result; -} - -export async function getCurrentClusterConfig(kubectl: Kubectl): Promise { - const kubeConfig = await getKubeconfig(kubectl); - if (!kubeConfig || !kubeConfig.clusters || !kubeConfig.contexts) { - return undefined; - } - const contextConfig = kubeConfig.contexts.find((context) => context.name === kubeConfig['current-context'])!; - const clusterConfig = kubeConfig.clusters.find((cluster) => cluster.name === contextConfig.context.cluster)!; - return { - server: clusterConfig.cluster.server, - certificateAuthority: clusterConfig.cluster['certificate-authority'] - }; -} - -export async function getContexts(kubectl: Kubectl): Promise { - const kubectlConfig = await getKubeconfig(kubectl); - if (!kubectlConfig) { - return []; - } - const currentContext = kubectlConfig['current-context']; - const contexts = kubectlConfig.contexts || []; - return contexts.map((c) => { - return { - clusterName: c.context.cluster, - contextName: c.name, - userName: c.context.user, - active: c.name === currentContext - }; - }); -} - -export async function setContext(kubectl: Kubectl, targetContext: string): Promise { - const shellResult = await kubectl.invokeAsync(`config use-context ${targetContext}`); - if (!shellResult || shellResult.code !== 0) { - // TODO: Update error handling for now. - let errMsg = shellResult ? shellResult.stderr : localize('runKubectlFailed', 'Unable to run kubectl'); - vscode.window.showErrorMessage(localize('setClusterFailed', 'Failed to set \'{0}\' as current cluster: {1}', targetContext, errMsg)); - } -} - -export async function inferCurrentClusterType(kubectl: Kubectl): Promise { - let latestContextName = ''; - - const ctxsr = await kubectl.invokeAsync('config current-context'); - if (ctxsr && ctxsr.code === 0) { - latestContextName = ctxsr.stdout.trim(); - } else { - return ClusterType.Other; - } - - const cisr = await kubectl.invokeAsync('cluster-info'); - if (!cisr || cisr.code !== 0) { - return ClusterType.Unknown; - } - const masterInfos = cisr.stdout.split('\n') - .filter((s) => s.indexOf('master is running at') >= 0); - - if (masterInfos.length === 0) { - return ClusterType.Other; - } - - const masterInfo = masterInfos[0]; - if (masterInfo.indexOf('azmk8s.io') >= 0 || masterInfo.indexOf('azure.com') >= 0) { - return ClusterType.AKS; - } - - if (latestContextName) { - const gcsr = await kubectl.invokeAsync(`config get-contexts ${latestContextName}`); - if (gcsr && gcsr.code === 0) { - if (gcsr.stdout.indexOf('minikube') >= 0) { - return ClusterType.Minikube; - } - } - } - - return ClusterType.Other; -} \ No newline at end of file diff --git a/extensions/big-data-cluster/src/kubectl/sqlServerBigDataClusterChannel.ts b/extensions/big-data-cluster/src/kubectl/sqlServerBigDataClusterChannel.ts deleted file mode 100644 index dd93751c49..0000000000 --- a/extensions/big-data-cluster/src/kubectl/sqlServerBigDataClusterChannel.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as nls from 'vscode-nls'; -const localize = nls.loadMessageBundle(); - -export interface ISqlServerBigDataClusterChannel { - showOutput(message: any, title?: string): void; -} - -const outputChannelName = localize('bigDataClusterOutputChannel', 'SQL Server big data cluster'); -class SqlServerBigDataCluster implements ISqlServerBigDataClusterChannel { - private readonly channel: vscode.OutputChannel = vscode.window.createOutputChannel(outputChannelName); - - showOutput(message: any, title?: string): void { - if (title) { - const simplifiedTime = (new Date()).toISOString().replace(/z|t/gi, ' ').trim(); // YYYY-MM-DD HH:mm:ss.sss - const hightlightingTitle = `[${title} ${simplifiedTime}]`; - this.channel.appendLine(hightlightingTitle); - } - this.channel.appendLine(message); - this.channel.show(); - } -} - -export const sqlserverbigdataclusterchannel: ISqlServerBigDataClusterChannel = new SqlServerBigDataCluster(); diff --git a/extensions/big-data-cluster/src/main.ts b/extensions/big-data-cluster/src/main.ts deleted file mode 100644 index 4d3a864182..0000000000 --- a/extensions/big-data-cluster/src/main.ts +++ /dev/null @@ -1,60 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import vscode = require('vscode'); -import * as nls from 'vscode-nls'; -const localize = nls.loadMessageBundle(); - -import { MainController } from './mainController'; - -import { fs } from './utility/fs'; - -import { host } from './kubectl/host'; -import { sqlserverbigdataclusterchannel } from './kubectl/sqlServerBigDataClusterChannel'; -import { shell, Shell } from './utility/shell'; -import { CheckPresentMessageMode, create as kubectlCreate } from './kubectl/kubectl'; -import { installKubectl } from './installer/installer'; -import { Errorable, failed } from './interfaces'; - -const kubectl = kubectlCreate(host, fs, shell, installDependencies); -export let controller: MainController; - -export function activate(context: vscode.ExtensionContext) { - controller = new MainController(context, kubectl); - controller.activate(); -} - -// this method is called when your extension is deactivated -export function deactivate(): void { - if (controller) { - controller.deactivate(); - } -} - -export async function installDependencies() { - const gotKubectl = await kubectl.checkPresent(CheckPresentMessageMode.Silent); - - - const installPromises = [ - installDependency('kubectl', gotKubectl, installKubectl) - ]; - - await Promise.all(installPromises); - - sqlserverbigdataclusterchannel.showOutput(localize('done', 'Done')); -} - -async function installDependency(name: string, alreadyGot: boolean, installFunc: (shell: Shell) => Promise>): Promise { - if (alreadyGot) { - sqlserverbigdataclusterchannel.showOutput(localize('dependencyInstalled', '{0} already installed...', name)); - } else { - sqlserverbigdataclusterchannel.showOutput(localize('installingDependency', 'Installing {0}...', name)); - const result = await installFunc(shell); - if (failed(result)) { - sqlserverbigdataclusterchannel.showOutput(localize('installingDependencyFailed', 'Unable to install {0}: {1}', name, result.error[0])); - } - } -} \ No newline at end of file diff --git a/extensions/big-data-cluster/src/mainController.ts b/extensions/big-data-cluster/src/mainController.ts deleted file mode 100644 index 02ca5c882c..0000000000 --- a/extensions/big-data-cluster/src/mainController.ts +++ /dev/null @@ -1,36 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as vscode from 'vscode'; -import { CreateClusterWizard } from './wizards/create-cluster/createClusterWizard'; -import { Kubectl } from './kubectl/kubectl'; -/** - * The main controller class that initializes the extension - */ -export class MainController { - protected _context: vscode.ExtensionContext; - protected _kubectl: Kubectl; - - public constructor(context: vscode.ExtensionContext, kubectl: Kubectl) { - this._context = context; - this._kubectl = kubectl; - } - - /** - * Activates the extension - */ - public activate(): void { - vscode.commands.registerCommand('mssql.cluster.create', () => { - let wizard = new CreateClusterWizard(this._context, this._kubectl); - wizard.open(); - }); - } - - /** - * Deactivates the extension - */ - public deactivate(): void { } -} diff --git a/extensions/big-data-cluster/src/scripting/scripting.ts b/extensions/big-data-cluster/src/scripting/scripting.ts deleted file mode 100644 index c21e39991e..0000000000 --- a/extensions/big-data-cluster/src/scripting/scripting.ts +++ /dev/null @@ -1,73 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import { fs } from '../utility/fs'; -import { Shell } from '../utility/shell'; -import * as vscode from 'vscode'; -import * as path from 'path'; -import * as os from 'os'; -import mkdirp = require('mkdirp'); -import { Kubectl, baseKubectlPath } from '../kubectl/kubectl'; -import { KubectlContext } from '../kubectl/kubectlUtils'; - -export interface Scriptable { - getScriptProperties(): Promise>; - getTargetKubectlContext(): KubectlContext; -} - -export interface ScriptingDictionary { - [name: string]: V; -} - -const deployFilePrefix: string = 'mssql-bdc-deploy'; -export class ScriptGenerator { - - private _shell: Shell; - private _kubectl: Kubectl; - - private _kubectlPath: string; - constructor(_kubectl: Kubectl) { - this._kubectl = _kubectl; - this._shell = this._kubectl.getContext().shell; - this._kubectlPath = baseKubectlPath(this._kubectl.getContext()); - } - - public async generateDeploymentScript(scriptable: Scriptable): Promise { - let targetClusterName = scriptable.getTargetKubectlContext().clusterName; - let targetContextName = scriptable.getTargetKubectlContext().contextName; - - let timestamp = new Date().getTime(); - let deployFolder = this.getDeploymentFolder(this._shell); - let deployFileSuffix = this._shell.isWindows() ? `.bat` : `.sh`; - let deployFileName = `${deployFilePrefix}-${targetClusterName}-${timestamp}${deployFileSuffix}`; - let deployFilePath = path.join(deployFolder, deployFileName); - - let envVars = ''; - let propertiesDict = await scriptable.getScriptProperties(); - for (let key in propertiesDict) { - let value = propertiesDict[key]; - envVars += this._shell.isWindows() ? `Set ${key} = ${value}\n` : `export ${key} = ${value}\n`; - } - envVars += os.EOL; - - let kubeContextcommand = `${this._kubectlPath} config use-context ${targetContextName}\n`; - // Todo: The API for mssqlctl may change per version, so need a version check to use proper syntax. - let deployCommand = `mssqlctl create cluster ${targetClusterName}\n`; - - let deployContent = envVars + kubeContextcommand + deployCommand; - - mkdirp.sync(deployFolder); - await fs.writeFile(deployFilePath, deployContent, handleError); - } - - public getDeploymentFolder(shell: Shell): string { - return path.join(shell.home(), `.mssql-bdc/deployment`); - } -} - -const handleError = (err: NodeJS.ErrnoException) => { - if (err) { - vscode.window.showErrorMessage(err.message); - } -}; \ No newline at end of file diff --git a/extensions/big-data-cluster/src/typings/ref.d.ts b/extensions/big-data-cluster/src/typings/refs.d.ts similarity index 93% rename from extensions/big-data-cluster/src/typings/ref.d.ts rename to extensions/big-data-cluster/src/typings/refs.d.ts index 4d46be908b..d79b8a564b 100644 --- a/extensions/big-data-cluster/src/typings/ref.d.ts +++ b/extensions/big-data-cluster/src/typings/refs.d.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// /// /// +/// /// \ No newline at end of file diff --git a/extensions/big-data-cluster/src/utility/fs.ts b/extensions/big-data-cluster/src/utility/fs.ts deleted file mode 100644 index 9ba7b63fe1..0000000000 --- a/extensions/big-data-cluster/src/utility/fs.ts +++ /dev/null @@ -1,65 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import * as sysfs from 'fs'; - -export interface FS { - existsSync(path: string): boolean; - readFile(filename: string, encoding: string, callback: (err: NodeJS.ErrnoException, data: string) => void): void; - readFileSync(filename: string, encoding: string): string; - readFileToBufferSync(filename: string): Buffer; - writeFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void; - writeFileSync(filename: string, data: any): void; - dirSync(path: string): string[]; - unlinkAsync(path: string): Promise; - existsAsync(path: string): Promise; - openAsync(path: string, flags: string): Promise; - statSync(path: string): sysfs.Stats; -} - -export const fs: FS = { - existsSync: (path) => sysfs.existsSync(path), - readFile: (filename, encoding, callback) => sysfs.readFile(filename, encoding, callback), - readFileSync: (filename, encoding) => sysfs.readFileSync(filename, encoding), - readFileToBufferSync: (filename) => sysfs.readFileSync(filename), - writeFile: (filename, data, callback) => sysfs.writeFile(filename, data, callback), - writeFileSync: (filename, data) => sysfs.writeFileSync(filename, data), - dirSync: (path) => sysfs.readdirSync(path), - - unlinkAsync: (path) => { - return new Promise((resolve, reject) => { - sysfs.unlink(path, (error) => { - if (error) { - reject(); - return; - } - - resolve(); - }); - }); - }, - - existsAsync: (path) => { - return new Promise((resolve) => { - sysfs.exists(path, (exists) => { - resolve(exists); - }); - }); - }, - - openAsync: (path, flags) => { - return new Promise((resolve, reject) => { - sysfs.open(path, flags, (error, _fd) => { - if (error) { - reject(); - return; - } - - resolve(); - }); - }); - }, - - statSync: (path) => sysfs.statSync(path) -}; diff --git a/extensions/big-data-cluster/src/utility/shell.ts b/extensions/big-data-cluster/src/utility/shell.ts deleted file mode 100644 index e27193e2bc..0000000000 --- a/extensions/big-data-cluster/src/utility/shell.ts +++ /dev/null @@ -1,204 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import * as vscode from 'vscode'; -import * as shelljs from 'shelljs'; -import * as path from 'path'; -import { getActiveKubeconfig, getToolPath } from '../config/config'; -import { host } from '../kubectl/host'; - -export enum Platform { - Windows, - MacOS, - Linux, - Unsupported, -} - -export interface ExecCallback extends shelljs.ExecCallback { } - -export interface Shell { - isWindows(): boolean; - isUnix(): boolean; - platform(): Platform; - home(): string; - combinePath(basePath: string, relativePath: string): string; - fileUri(filePath: string): vscode.Uri; - execOpts(): any; - exec(cmd: string, stdin?: string): Promise; - execCore(cmd: string, opts: any, stdin?: string): Promise; - unquotedPath(path: string): string; - which(bin: string): string | null; - cat(path: string): string; - ls(path: string): string[]; -} - -export const shell: Shell = { - isWindows: isWindows, - isUnix: isUnix, - platform: platform, - home: home, - combinePath: combinePath, - fileUri: fileUri, - execOpts: execOpts, - exec: exec, - execCore: execCore, - unquotedPath: unquotedPath, - which: which, - cat: cat, - ls: ls, -}; - -const WINDOWS: string = 'win32'; - -export interface ShellResult { - readonly code: number; - readonly stdout: string; - readonly stderr: string; -} - -export type ShellHandler = (code: number, stdout: string, stderr: string) => void; - -function isWindows(): boolean { - return (process.platform === WINDOWS); -} - -function isUnix(): boolean { - return !isWindows(); -} - -function platform(): Platform { - switch (process.platform) { - case 'win32': return Platform.Windows; - case 'darwin': return Platform.MacOS; - case 'linux': return Platform.Linux; - default: return Platform.Unsupported; - } -} - -function concatIfBoth(s1: string | undefined, s2: string | undefined): string | undefined { - return s1 && s2 ? s1.concat(s2) : undefined; -} - -function home(): string { - return process.env['HOME'] || - concatIfBoth(process.env['HOMEDRIVE'], process.env['HOMEPATH']) || - process.env['USERPROFILE'] || - ''; -} - -function combinePath(basePath: string, relativePath: string) { - let separator = '/'; - if (isWindows()) { - relativePath = relativePath.replace(/\//g, '\\'); - separator = '\\'; - } - return basePath + separator + relativePath; -} - -function isWindowsFilePath(filePath: string) { - return filePath[1] === ':' && filePath[2] === '\\'; -} - -function fileUri(filePath: string): vscode.Uri { - if (isWindowsFilePath(filePath)) { - return vscode.Uri.parse('file:///' + filePath.replace(/\\/g, '/')); - } - return vscode.Uri.parse('file://' + filePath); -} - -function execOpts(): any { - let env = process.env; - if (isWindows()) { - env = Object.assign({}, env, { HOME: home() }); - } - env = shellEnvironment(env); - const opts = { - cwd: vscode.workspace.rootPath, - env: env, - async: true - }; - return opts; -} - -async function exec(cmd: string, stdin?: string): Promise { - try { - return await execCore(cmd, execOpts(), stdin); - } catch (ex) { - vscode.window.showErrorMessage(ex); - return undefined; - } -} - -function execCore(cmd: string, opts: any, stdin?: string): Promise { - return new Promise((resolve) => { - const proc = shelljs.exec(cmd, opts, (code, stdout, stderr) => resolve({ code: code, stdout: stdout, stderr: stderr })); - if (stdin) { - proc.stdin.end(stdin); - } - }); -} - -function unquotedPath(path: string): string { - if (isWindows() && path && path.length > 1 && path.startsWith('"') && path.endsWith('"')) { - return path.substring(1, path.length - 1); - } - return path; -} - -export function shellEnvironment(baseEnvironment: any): any { - const env = Object.assign({}, baseEnvironment); - const pathVariable = pathVariableName(env); - for (const tool of ['kubectl']) { - const toolPath = getToolPath(host, shell, tool); - if (toolPath) { - const toolDirectory = path.dirname(toolPath); - const currentPath = env[pathVariable]; - env[pathVariable] = toolDirectory + (currentPath ? `${pathEntrySeparator()}${currentPath}` : ''); - } - } - - const kubeconfig = getActiveKubeconfig(); - if (kubeconfig) { - env['KUBECONFIG'] = kubeconfig; - } - - return env; -} - -function pathVariableName(env: any): string { - if (isWindows()) { - for (const v of Object.keys(env)) { - if (v.toLowerCase() === 'path') { - return v; - } - } - } - return 'PATH'; -} - -function pathEntrySeparator() { - return isWindows() ? ';' : ':'; -} - -function which(bin: string): string | null { - return shelljs.which(bin); -} - -function cat(path: string): string { - return shelljs.cat(path); -} - -function ls(path: string): string[] { - return shelljs.ls(path); -} - -export function shellMessage(sr: ShellResult | undefined, invocationFailureMessage: string): string { - if (!sr) { - return invocationFailureMessage; - } - return sr.code === 0 ? sr.stdout : sr.stderr; -} diff --git a/extensions/big-data-cluster/src/wizards/create-cluster/createClusterModel.ts b/extensions/big-data-cluster/src/wizards/create-cluster/createClusterModel.ts deleted file mode 100644 index c0c9ed1ed5..0000000000 --- a/extensions/big-data-cluster/src/wizards/create-cluster/createClusterModel.ts +++ /dev/null @@ -1,328 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import { TargetClusterType, ClusterPorts, ClusterType, ContainerRegistryInfo, TargetClusterTypeInfo, ToolInfo, ToolInstallationStatus, ClusterProfile, PoolConfiguration, SQLServerMasterConfiguration, ClusterPoolType, ClusterResourceSummary } from '../../interfaces'; -import { getContexts, KubectlContext, setContext, inferCurrentClusterType } from '../../kubectl/kubectlUtils'; -import { Kubectl } from '../../kubectl/kubectl'; -import { Scriptable, ScriptingDictionary } from '../../scripting/scripting'; -import * as nls from 'vscode-nls'; -import * as os from 'os'; -import * as path from 'path'; - -const localize = nls.loadMessageBundle(); - -export class CreateClusterModel implements Scriptable { - - private _tmp_tools_installed: boolean = false; - private scriptingProperties: ScriptingDictionary = {}; - constructor(private _kubectl: Kubectl) { - } - - public async loadClusters(): Promise { - return await getContexts(this._kubectl); - } - - public async changeKubernetesContext(targetContext: string): Promise { - await setContext(this._kubectl, targetContext); - } - - public getDefaultPorts(): Thenable { - let promise = new Promise(resolve => { - resolve({ - sql: '31433', - knox: '30443', - controller: '30888', - proxy: '30909', - grafana: '30119', - kibana: '30999' - }); - }); - return promise; - } - - public getDefaultContainerRegistryInfo(): Thenable { - let promise = new Promise(resolve => { - resolve({ - registry: 'private-repo.microsoft.com', - repository: 'mssql-private-preview', - imageTag: 'latest' - }); - }); - return promise; - } - - public getAllTargetClusterTypeInfo(): Thenable { - let promise = new Promise(resolve => { - let aksCluster: TargetClusterTypeInfo = { - enabled: false, - type: TargetClusterType.NewAksCluster, - name: localize('bdc-create.AKSClusterCardText', 'New AKS Cluster'), - fullName: localize('bdc-create.AKSClusterFullName', 'New Azure Kubernetes Service cluster'), - description: localize('bdc-create.AKSClusterDescription', - 'This option configures new Azure Kubernetes Service (AKS) for SQL Server big data cluster deployments. AKS makes it simple to create, configure and manage a cluster of virutal machines that are preconfigured with a Kubernetes cluster to run containerized applications.'), - iconPath: { - dark: 'images/aks.svg', - light: 'images/aks.svg' - } - }; - - let existingCluster: TargetClusterTypeInfo = { - enabled: true, - type: TargetClusterType.ExistingKubernetesCluster, - name: localize('bdc-create.ExistingClusterCardText', 'Existing Cluster'), - fullName: localize('bdc-create.ExistingClusterFullName', 'Existing Kubernetes cluster'), - description: localize('bdc-create.ExistingClusterDescription', 'This option assumes you already have a Kubernetes cluster installed, Once a prerequisite check is done, ensure the correct cluster context is selected.'), - iconPath: { - dark: 'images/kubernetes.svg', - light: 'images/kubernetes.svg' - } - }; - resolve([aksCluster, existingCluster]); - }); - return promise; - } - - public getRequiredToolStatus(): Thenable { - let kubeCtl = { - name: 'kubectl', - description: 'Tool used for managing the Kubernetes cluster', - version: '', - status: ToolInstallationStatus.Installed - }; - let mssqlCtl = { - name: 'mssqlctl', - description: 'Command-line tool for installing and managing the SQL Server big data cluster', - version: '', - status: ToolInstallationStatus.Installed - }; - let azureCli = { - name: 'Azure CLI', - description: 'Tool used for managing Azure services', - version: '', - status: this._tmp_tools_installed ? ToolInstallationStatus.Installed : ToolInstallationStatus.NotInstalled - }; - let promise = new Promise(resolve => { - setTimeout(() => { - let tools = this.targetClusterType === TargetClusterType.ExistingKubernetesCluster ? [kubeCtl, mssqlCtl] : [kubeCtl, mssqlCtl, azureCli]; - resolve(tools); - }, 1000); - }); - return promise; - } - - public installTool(tool: ToolInfo): Thenable { - let promise = new Promise(resolve => { - setTimeout(() => { - tool.status = ToolInstallationStatus.Installed; - this._tmp_tools_installed = true; - resolve(); - }, 1000); - }); - return promise; - } - - public getDefaultKubeConfigPath(): string { - return path.join(os.homedir(), '.kube', 'config'); - } - - public clusterName: string; - - public targetClusterType: TargetClusterType; - - public selectedCluster: KubectlContext; - - public adminUserName: string; - - public adminPassword: string; - - public sqlPort: string; - - public knoxPort: string; - - public controllerPort: string; - - public proxyPort: string; - - public grafanaPort: string; - - public kibanaPort: string; - - public containerRegistry: string; - - public containerRepository: string; - - public containerImageTag: string; - - public containerRegistryUserName: string; - - public containerRegistryPassword: string; - - public profile: ClusterProfile; - - public async getTargetClusterPlatform(targetContextName: string): Promise { - await setContext(this._kubectl, targetContextName); - let clusterType = await inferCurrentClusterType(this._kubectl); - - switch (clusterType) { - case ClusterType.AKS: - return 'aks'; - case ClusterType.Minikube: - return 'minikube'; - case ClusterType.Other: - default: - return 'kubernetes'; - } - } - - public async getScriptProperties(): Promise> { - - // Cluster settings - this.scriptingProperties['CLUSTER_NAME'] = this.selectedCluster.clusterName; - this.scriptingProperties['CLUSTER_PLATFORM'] = await this.getTargetClusterPlatform(this.selectedCluster.contextName); - - // Default pool count for now. TODO: Update from user input - this.scriptingProperties['CLUSTER_DATA_POOL_REPLICAS'] = '1'; - this.scriptingProperties['CLUSTER_COMPUTE_POOL_REPLICAS'] = '2'; - this.scriptingProperties['CLUSTER_STORAGE_POOL_REPLICAS'] = '3'; - - // SQL Server settings - this.scriptingProperties['CONTROLLER_USERNAME'] = this.adminUserName; - this.scriptingProperties['CONTROLLER_PASSWORD'] = this.adminPassword; - this.scriptingProperties['KNOX_PASSWORD'] = this.adminPassword; - this.scriptingProperties['MSSQL_SA_PASSWORD'] = this.adminPassword; - - // docker settings - this.scriptingProperties['DOCKER_REPOSITORY'] = this.containerRepository; - this.scriptingProperties['DOCKER_REGISTRY'] = this.containerRegistry; - this.scriptingProperties['DOCKER_PASSWORD'] = this.containerRegistryPassword; - this.scriptingProperties['DOCKER_USERNAME'] = this.containerRegistryUserName; - this.scriptingProperties['DOCKER_IMAGE_TAG'] = this.containerImageTag; - - // port settings - this.scriptingProperties['MASTER_SQL_PORT'] = this.sqlPort; - this.scriptingProperties['KNOX_PORT'] = this.knoxPort; - this.scriptingProperties['GRAFANA_PORT'] = this.grafanaPort; - this.scriptingProperties['KIBANA_PORT'] = this.kibanaPort; - - return this.scriptingProperties; - } - - public getTargetKubectlContext(): KubectlContext { - return this.selectedCluster; - } - - public getClusterResource(): Thenable { - let promise = new Promise(resolve => { - setTimeout(() => { - let resoureSummary: ClusterResourceSummary = { - hardwareLabels: [ - { - name: '', - totalNodes: 10, - totalCores: 22, - totalDisks: 128, - totalMemoryInGB: 77 - }, - { - name: '#data', - totalNodes: 4, - totalCores: 22, - totalDisks: 200, - totalMemoryInGB: 100 - }, - { - name: '#compute', - totalNodes: 12, - totalCores: 124, - totalDisks: 24, - totalMemoryInGB: 100 - }, - { - name: '#premium', - totalNodes: 10, - totalCores: 100, - totalDisks: 200, - totalMemoryInGB: 770 - } - ] - }; - resolve(resoureSummary); - }, 1000); - }); - return promise; - } - - public getProfiles(): Thenable { - let promise = new Promise(resolve => { - setTimeout(() => { - let profiles: ClusterProfile[] = []; - profiles.push({ - name: 'Basic', - sqlServerMasterConfiguration: this.createSQLPoolConfiguration(1, 1), - computePoolConfiguration: this.createComputePoolConfiguration(2), - dataPoolConfiguration: this.createDataPoolConfiguration(2), - storagePoolConfiguration: this.createStoragePoolConfiguration(2), - sparkPoolConfiguration: this.createSparkPoolConfiguration(2) - }); - profiles.push({ - name: 'Standard', - sqlServerMasterConfiguration: this.createSQLPoolConfiguration(3, 9), - computePoolConfiguration: this.createComputePoolConfiguration(5), - dataPoolConfiguration: this.createDataPoolConfiguration(5), - storagePoolConfiguration: this.createStoragePoolConfiguration(5), - sparkPoolConfiguration: this.createSparkPoolConfiguration(5) - }); - profiles.push({ - name: 'Premium', - sqlServerMasterConfiguration: this.createSQLPoolConfiguration(5, 9), - computePoolConfiguration: this.createComputePoolConfiguration(7), - dataPoolConfiguration: this.createDataPoolConfiguration(7), - storagePoolConfiguration: this.createStoragePoolConfiguration(7), - sparkPoolConfiguration: this.createSparkPoolConfiguration(7) - }); - resolve(profiles); - }, 1000); - }); - return promise; - } - - private createSQLPoolConfiguration(scale: number, maxScale: number): SQLServerMasterConfiguration { - return { - type: ClusterPoolType.SQL, - engineOnly: false, - scale: scale, - maxScale: maxScale - }; - } - - private createComputePoolConfiguration(scale: number): PoolConfiguration { - return { - type: ClusterPoolType.Compute, - scale: scale - }; - } - - private createDataPoolConfiguration(scale: number): PoolConfiguration { - return { - type: ClusterPoolType.Data, - scale: scale - }; - } - - private createStoragePoolConfiguration(scale: number): PoolConfiguration { - return { - type: ClusterPoolType.Storage, - scale: scale - }; - } - - private createSparkPoolConfiguration(scale: number): PoolConfiguration { - return { - type: ClusterPoolType.Spark, - scale: scale - }; - } -} diff --git a/extensions/big-data-cluster/src/wizards/create-cluster/createClusterWizard.ts b/extensions/big-data-cluster/src/wizards/create-cluster/createClusterWizard.ts deleted file mode 100644 index e31cc25648..0000000000 --- a/extensions/big-data-cluster/src/wizards/create-cluster/createClusterWizard.ts +++ /dev/null @@ -1,54 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import { CreateClusterModel } from './createClusterModel'; -import { SelectExistingClusterPage } from './pages/selectExistingClusterPage'; -import { SummaryPage } from './pages/summaryPage'; -import { SettingsPage } from './pages/settingsPage'; -import { ClusterProfilePage } from './pages/clusterProfilePage'; -import { ExtensionContext } from 'vscode'; -import { WizardBase } from '../wizardBase'; -import * as nls from 'vscode-nls'; -import { Kubectl } from '../../kubectl/kubectl'; -import { SelectTargetClusterTypePage } from './pages/selectTargetClusterTypePage'; -import { ScriptGenerator } from '../../scripting/scripting'; -const localize = nls.loadMessageBundle(); - -export class CreateClusterWizard extends WizardBase { - private scripter: ScriptGenerator; - constructor(context: ExtensionContext, kubectl: Kubectl) { - let model = new CreateClusterModel(kubectl); - super(model, context, localize('bdc-create.wizardTitle', 'Create a big data cluster')); - this.scripter = new ScriptGenerator(kubectl); - } - - protected initialize(): void { - let settingsPage = new SettingsPage(this); - let clusterProfilePage = new ClusterProfilePage(this); - let selectTargetClusterPage = new SelectExistingClusterPage(this); - let summaryPage = new SummaryPage(this); - let targetClusterTypePage = new SelectTargetClusterTypePage(this); - this.setPages([targetClusterTypePage, selectTargetClusterPage, settingsPage, clusterProfilePage, summaryPage]); - - this.wizardObject.generateScriptButton.label = localize('bdc-create.generateScriptsButtonText', 'Generate Scripts'); - this.wizardObject.generateScriptButton.hidden = false; - this.wizardObject.doneButton.label = localize('bdc-create.createClusterButtonText', 'Create'); - - this.registerDisposable(this.wizardObject.generateScriptButton.onClick(async () => { - this.wizardObject.generateScriptButton.enabled = false; - this.scripter.generateDeploymentScript(this.model).then(() => { - this.wizardObject.generateScriptButton.enabled = true; - //TODO: Add error handling. - }); - })); - } - - protected onCancel(): void { - } - - protected onOk(): void { - } -} diff --git a/extensions/big-data-cluster/src/wizards/create-cluster/pages/clusterProfilePage.ts b/extensions/big-data-cluster/src/wizards/create-cluster/pages/clusterProfilePage.ts deleted file mode 100644 index ae8d384e1f..0000000000 --- a/extensions/big-data-cluster/src/wizards/create-cluster/pages/clusterProfilePage.ts +++ /dev/null @@ -1,389 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 { WizardPageBase } from '../../wizardPageBase'; -import { CreateClusterWizard } from '../createClusterWizard'; -import * as nls from 'vscode-nls'; -import { ClusterProfile, PoolConfiguration, ClusterPoolType, SQLServerMasterConfiguration, ClusterResourceSummary } from '../../../interfaces'; - - -const localize = nls.loadMessageBundle(); -const LabelWidth = '200px'; -const InputWidth = '300px'; - -export class ClusterProfilePage extends WizardPageBase { - private view: azdata.ModelView; - private clusterProfiles: ClusterProfile[]; - private poolList: azdata.FlexContainer; - private detailContainer: azdata.FlexContainer; - private clusterResourceView: azdata.GroupContainer; - private poolListMap = {}; - private clusterResourceContainer: azdata.FlexContainer; - private clusterResourceLoadingComponent: azdata.LoadingComponent; - private clusterResource: ClusterResourceSummary; - - - constructor(wizard: CreateClusterWizard) { - super(localize('bdc-create.clusterProfilePageTitle', 'Select a cluster profile'), - localize('bdc-create.clusterProfilePageDescription', 'Select your requirement and we will provide you a pre-defined default scaling. You can later go to cluster configuration and customize it.'), - wizard); - } - - public onEnter(): void { - this.updatePoolList(); - this.clusterResourceLoadingComponent.loading = true; - this.wizard.model.getClusterResource().then((resource) => { - this.clusterResource = resource; - this.initializeClusterResourceView(); - }); - this.wizard.wizardObject.registerNavigationValidator(() => { - return true; - }); - } - - protected initialize(view: azdata.ModelView): Thenable { - this.view = view; - let fetchProfilePromise = this.wizard.model.getProfiles().then(p => { this.clusterProfiles = p; }); - return Promise.all([fetchProfilePromise]).then(() => { - this.wizard.model.profile = this.clusterProfiles[0]; - this.clusterResourceView = this.view.modelBuilder.groupContainer().withLayout({ - header: localize('bdc-create.TargetClusterOverview', 'Target cluster scale overview'), - collapsed: true, - collapsible: true - }).component(); - - this.clusterResourceContainer = this.view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); - this.clusterResourceLoadingComponent = this.view.modelBuilder.loadingComponent().withItem(this.clusterResourceContainer).component(); - this.clusterResourceView.addItem(this.clusterResourceLoadingComponent); - - let profileLabel = view.modelBuilder.text().withProperties({ value: localize('bdc-create.clusterProfileLabel', 'Deployment profile') }).component(); - let profileDropdown = view.modelBuilder.dropDown().withProperties({ - values: this.clusterProfiles.map(profile => profile.name), - width: '300px' - }).component(); - let dropdownRow = this.view.modelBuilder.flexContainer().withItems([profileLabel, profileDropdown], { CSSStyles: { 'margin-right': '30px' } }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component(); - let poolContainer = this.view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', width: '100%', height: '100%' }).component(); - this.poolList = this.view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '300px', height: '100%' }).component(); - poolContainer.addItem(this.poolList, { - CSSStyles: { - 'border-top-style': 'solid', - 'border-top-width': '2px', - 'border-right-style': 'solid', - 'border-right-width': '2px', - 'border-color': 'lightgray' - } - }); - - this.detailContainer = this.view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '760px', height: '100%' }).component(); - poolContainer.addItem(this.detailContainer, { - CSSStyles: { - 'border-top-style': 'solid', - 'border-top-width': '2px', - 'border-color': 'lightgray' - } - }); - - this.wizard.registerDisposable(profileDropdown.onValueChanged(() => { - let profiles = this.clusterProfiles.filter(p => profileDropdown.value === p.name); - if (profiles && profiles.length === 1) { - this.wizard.model.profile = profiles[0]; - this.updatePoolList(); - this.clearPoolDetail(); - } - })); - - this.initializePoolList(); - - let pageContainer = this.view.modelBuilder.flexContainer().withLayout({ - flexFlow: 'column', - height: '800px' - }).component(); - pageContainer.addItem(this.clusterResourceView, { - flex: '0 0 auto', - CSSStyles: { - 'margin-bottom': '20px', - 'padding-bottom': '5px', - 'padding-top': '5px' - } - }); - pageContainer.addItem(dropdownRow, { - flex: '0 0 auto', - CSSStyles: { 'margin-bottom': '10px' } - }); - pageContainer.addItem(poolContainer, { - flex: '1 1 auto', - CSSStyles: { - 'display': 'flex' - } - }); - let formBuilder = view.modelBuilder.formContainer(); - let form = formBuilder.withFormItems([{ - title: '', - component: pageContainer - }], { - horizontal: false, - componentWidth: '100%' - }).component(); - - return view.initializeModel(form); - }); - } - - private initializeClusterResourceView(): void { - this.clusterResourceContainer.clearItems(); - let text = this.view.modelBuilder.text().withProperties({ value: localize('bdc-create.HardwareProfileText', 'Hardware profile') }).component(); - let height = (this.clusterResource.hardwareLabels.length * 25) + 30; - let labelColumn: azdata.TableColumn = { - value: localize('bdc-create.HardwareLabelColumnName', 'Label'), - width: 100 - }; - let totalNodesColumn: azdata.TableColumn = { - value: localize('bdc-create.TotalNodesColumnName', 'Nodes'), - width: 50 - }; - let totalCoresColumn: azdata.TableColumn = { - value: localize('bdc-create.TotalCoresColumnName', 'Cores'), - width: 50 - }; - let totalMemoryColumn: azdata.TableColumn = { - value: localize('bdc-create.TotalMemoryColumnName', 'Memory'), - width: 50 - }; - let totalDisksColumn: azdata.TableColumn = { - value: localize('bdc-create.TotalDisksColumnName', 'Disks'), - width: 50 - }; - - let table = this.view.modelBuilder.table().withProperties({ - height: `${height}px`, - data: this.clusterResource.hardwareLabels.map(label => [label.name, label.totalNodes, label.totalCores, label.totalMemoryInGB, label.totalDisks]), - columns: [labelColumn, totalNodesColumn, totalCoresColumn, totalMemoryColumn, totalDisksColumn], - width: '300px' - - }).component(); - this.clusterResourceContainer.addItems([text, table]); - this.clusterResourceLoadingComponent.loading = false; - } - - private initializePoolList(): void { - let pools = [this.wizard.model.profile.sqlServerMasterConfiguration, - this.wizard.model.profile.computePoolConfiguration, - this.wizard.model.profile.dataPoolConfiguration, - this.wizard.model.profile.sparkPoolConfiguration, - this.wizard.model.profile.storagePoolConfiguration]; - pools.forEach(pool => { - let poolSummaryButton = this.view.modelBuilder.divContainer().withProperties({ clickable: true }).component(); - let container = this.view.modelBuilder.flexContainer().component(); - this.wizard.registerDisposable(poolSummaryButton.onDidClick(() => { - this.clearPoolDetail(); - let currentPool: PoolConfiguration; - switch (pool.type) { - case ClusterPoolType.SQL: - currentPool = this.wizard.model.profile.sqlServerMasterConfiguration; - break; - case ClusterPoolType.Compute: - currentPool = this.wizard.model.profile.computePoolConfiguration; - break; - case ClusterPoolType.Data: - currentPool = this.wizard.model.profile.dataPoolConfiguration; - break; - case ClusterPoolType.Storage: - currentPool = this.wizard.model.profile.storagePoolConfiguration; - break; - case ClusterPoolType.Spark: - currentPool = this.wizard.model.profile.sparkPoolConfiguration; - break; - default: - break; - } - if (currentPool) { - this.detailContainer.addItem(this.createPoolConfigurationPart(currentPool), { CSSStyles: { 'margin-left': '10px' } }); - } - })); - - let text = this.view.modelBuilder.text().component(); - this.poolListMap[pool.type] = text; - text.width = '250px'; - let chrevron = this.view.modelBuilder.text().withProperties({ value: '>' }).component(); - chrevron.width = '30px'; - container.addItem(text); - container.addItem(chrevron, { - CSSStyles: { - 'font-size': '20px', - 'line-height': '0px' - } - }); - poolSummaryButton.addItem(container); - this.poolList.addItem(poolSummaryButton, { - CSSStyles: { - 'border-bottom-style': 'solid', - 'border-bottom-width': '1px', - 'border-color': 'lightgray', - 'cursor': 'pointer' - } - }); - }); - } - - private createPoolConfigurationPart(configuration: PoolConfiguration): azdata.Component { - let container = this.view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); - switch (configuration.type) { - case ClusterPoolType.SQL: - this.createSQLConfigurationPart(container, configuration as SQLServerMasterConfiguration); - break; - default: - this.createDefaultPoolConfigurationPart(container, configuration); - break; - } - return container; - } - - private createSQLConfigurationPart(container: azdata.FlexContainer, configuration: SQLServerMasterConfiguration): void { - this.createDefaultPoolConfigurationPart(container, configuration); - this.addFeatureSetRow(container, configuration); - } - - private createDefaultPoolConfigurationPart(container: azdata.FlexContainer, configuration: PoolConfiguration): void { - this.addPoolNameLabel(container, this.getPoolDisplayName(configuration.type)); - this.addPoolDescriptionLabel(container, this.getPoolDescription(configuration.type)); - this.addScaleRow(container, configuration); - this.addHardwareLabelRow(container, configuration); - } - - private addPoolNameLabel(container: azdata.FlexContainer, text: string): void { - let poolNameLabel = this.view.modelBuilder.text().withProperties({ value: text }).component(); - container.addItem(poolNameLabel, { - flex: '0 0 auto', CSSStyles: { - 'font-size': '13px', - 'font-weight': 'bold' - } - }); - } - - private addPoolDescriptionLabel(container: azdata.FlexContainer, text: string): void { - let label = this.view.modelBuilder.text().withProperties({ value: text }).component(); - container.addItem(label, { - flex: '0 0 auto', - CSSStyles: { - 'margin-bottom': '20px' - } - }); - } - - private addScaleRow(container: azdata.FlexContainer, configuration: PoolConfiguration): void { - let label = this.view.modelBuilder.text().withProperties({ value: localize('bdc-create.ScaleLabel', 'Scale') }).component(); - label.width = LabelWidth; - let input = this.view.modelBuilder.inputBox().withProperties({ - inputType: 'number', - value: configuration.scale.toString(), - min: 1, - max: configuration.maxScale - }).component(); - - this.wizard.registerDisposable(input.onTextChanged(() => { - configuration.scale = Number(input.value); - this.updatePoolList(); - })); - input.width = InputWidth; - let row = this.createRow([label, input]); - container.addItem(row); - } - - private addHardwareLabelRow(container: azdata.FlexContainer, configuration: PoolConfiguration): void { - let label = this.view.modelBuilder.text().withProperties({ value: localize('bdc-create.HardwareProfileLabel', 'Hardware profile label') }).component(); - label.width = LabelWidth; - let optionalValues = this.clusterResource.hardwareLabels.map(label => label.name); - configuration.hardwareLabel = configuration.hardwareLabel ? configuration.hardwareLabel : optionalValues[0]; - let input = this.view.modelBuilder.dropDown().withProperties({ value: configuration.hardwareLabel, values: optionalValues }).component(); - this.wizard.registerDisposable(input.onValueChanged(() => { - configuration.hardwareLabel = input.value.toString(); - })); - input.width = InputWidth; - let row = this.createRow([label, input]); - container.addItem(row); - } - - private addFeatureSetRow(container: azdata.FlexContainer, configuration: SQLServerMasterConfiguration): void { - const radioGroupName = 'featureset'; - let label = this.view.modelBuilder.text().withProperties({ value: localize('bdc-create.FeatureSetLabel', 'Feature set') }).component(); - label.width = LabelWidth; - let engineOnlyOption = this.view.modelBuilder.radioButton().withProperties({ label: localize('bdc-create.EngineOnlyText', 'Engine only'), name: radioGroupName, checked: configuration.engineOnly }).component(); - let engineWithFeaturesOption = this.view.modelBuilder.radioButton().withProperties({ label: localize('bdc-create.EngineWithFeaturesText', 'Engine with optional features'), name: radioGroupName, checked: !configuration.engineOnly }).component(); - let optionContainer = this.view.modelBuilder.divContainer().component(); - optionContainer.width = InputWidth; - optionContainer.addItems([engineOnlyOption, engineWithFeaturesOption]); - container.addItem(this.createRow([label, optionContainer])); - this.wizard.registerDisposable(engineOnlyOption.onDidClick(() => { - configuration.engineOnly = true; - })); - this.wizard.registerDisposable(engineWithFeaturesOption.onDidClick(() => { - configuration.engineOnly = false; - })); - } - - private createRow(items: azdata.Component[]): azdata.FlexContainer { - return this.view.modelBuilder.flexContainer().withItems(items, { - CSSStyles: { - 'margin-right': '5px' - } - }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component(); - } - - private getPoolDisplayName(poolType: ClusterPoolType): string { - switch (poolType) { - case ClusterPoolType.SQL: - return localize('bdc-create.SQLServerMasterDisplayName', 'SQL Server master'); - case ClusterPoolType.Compute: - return localize('bdc-create.ComputePoolDisplayName', 'Compute pool'); - case ClusterPoolType.Data: - return localize('bdc-create.DataPoolDisplayName', 'Data pool'); - case ClusterPoolType.Storage: - return localize('bdc-create.StoragePoolDisplayName', 'Storage pool'); - case ClusterPoolType.Spark: - return localize('bdc-create.SparkPoolDisplayName', 'Spark pool'); - default: - throw new Error('unknown pool type'); - } - } - - private getPoolDescription(poolType: ClusterPoolType): string { - switch (poolType) { - case ClusterPoolType.SQL: - return localize('bdc-create.SQLServerMasterDescription', 'The SQL Server instance provides an externally accessible TDS endpoint for the cluster'); - case ClusterPoolType.Compute: - return localize('bdc-create.ComputePoolDescription', 'TODO: Add description'); - case ClusterPoolType.Data: - return localize('bdc-create.DataPoolDescription', 'TODO: Add description'); - case ClusterPoolType.Storage: - return localize('bdc-create.StoragePoolDescription', 'TODO: Add description'); - case ClusterPoolType.Spark: - return localize('bdc-create.SparkPoolDescription', 'TODO: Add description'); - default: - throw new Error('unknown pool type'); - } - } - - private updatePoolList(): void { - let pools = [this.wizard.model.profile.sqlServerMasterConfiguration, - this.wizard.model.profile.computePoolConfiguration, - this.wizard.model.profile.dataPoolConfiguration, - this.wizard.model.profile.sparkPoolConfiguration, - this.wizard.model.profile.storagePoolConfiguration]; - pools.forEach(pool => { - let text = this.poolListMap[pool.type] as azdata.TextComponent; - if (text) { - text.value = localize({ - key: 'bdc-create.poolLabelTemplate', - comment: ['{0} is the pool name, {1} is the scale number'] - }, '{0} ({1})', this.getPoolDisplayName(pool.type), pool.scale); - } - }); - } - - private clearPoolDetail(): void { - this.detailContainer.clearItems(); - } -} diff --git a/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectExistingClusterPage.ts b/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectExistingClusterPage.ts deleted file mode 100644 index e996439d13..0000000000 --- a/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectExistingClusterPage.ts +++ /dev/null @@ -1,164 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * 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 os from 'os'; -import * as fs from 'fs'; -import { WizardPageBase } from '../../wizardPageBase'; -import { CreateClusterWizard } from '../createClusterWizard'; -import { setActiveKubeconfig } from '../../../config/config'; - -import * as nls from 'vscode-nls'; - -const localize = nls.loadMessageBundle(); -const ClusterRadioButtonGroupName = 'cluster'; - -export class SelectExistingClusterPage extends WizardPageBase { - private existingClusterControl: azdata.FlexContainer; - private clusterContextsLabel: azdata.TextComponent; - private errorLoadingClustersLabel: azdata.TextComponent; - private clusterContextList: azdata.DivContainer; - private clusterContextLoadingComponent: azdata.LoadingComponent; - private configFileInput: azdata.InputBoxComponent; - private browseFileButton: azdata.ButtonComponent; - private loadDefaultKubeConfigFile: boolean = true; - private view: azdata.ModelView; - - constructor(wizard: CreateClusterWizard) { - super(localize('bdc-create.selectTargetClusterPageTitle', 'Where do you want to deploy this SQL Server big data cluster?'), - localize('bdc-create.selectTargetClusterPageDescription', 'Select the kubeconfig file and then select a cluster context from the list'), - wizard); - } - - protected initialize(view: azdata.ModelView): Thenable { - this.view = view; - this.initExistingClusterControl(); - let formBuilder = view.modelBuilder.formContainer().withFormItems( - [ - { - component: this.existingClusterControl, - title: '' - } - ], - { - horizontal: false - } - ).withLayout({ width: '100%', height: '100%' }); - - let form = formBuilder.component(); - return view.initializeModel(form); - } - - public onEnter() { - if (this.loadDefaultKubeConfigFile) { - let defaultKubeConfigPath = this.wizard.model.getDefaultKubeConfigPath(); - if (fs.existsSync(defaultKubeConfigPath)) { - this.loadClusterContexts(defaultKubeConfigPath); - } - this.loadDefaultKubeConfigFile = false; - } - - this.wizard.wizardObject.registerNavigationValidator((e) => { - if (e.lastPage > e.newPage) { - this.wizard.wizardObject.message = null; - return true; - } - let clusterSelected = this.wizard.model.selectedCluster !== undefined; - if (!clusterSelected) { - this.wizard.wizardObject.message = { - text: localize('bdc-create.ClusterContextNotSelectedMessage', 'Please select a cluster context.'), - level: azdata.window.MessageLevel.Error - }; - } - return clusterSelected; - }); - } - - private initExistingClusterControl(): void { - let self = this; - const labelWidth = '150px'; - let configFileLabel = this.view.modelBuilder.text().withProperties({ value: localize('bdc-create.kubeConfigFileLabelText', 'Kube config file path') }).component(); - configFileLabel.width = labelWidth; - this.configFileInput = this.view.modelBuilder.inputBox().withProperties({ width: '300px' }).component(); - this.configFileInput.enabled = false; - this.browseFileButton = this.view.modelBuilder.button().withProperties({ label: localize('bdc-browseText', 'Browse'), width: '100px' }).component(); - let configFileContainer = this.view.modelBuilder.flexContainer() - .withLayout({ flexFlow: 'row', alignItems: 'baseline' }) - .withItems([configFileLabel, this.configFileInput, this.browseFileButton], { CSSStyles: { 'margin-right': '10px' } }).component(); - this.clusterContextsLabel = this.view.modelBuilder.text().withProperties({ value: localize('bdc-clusterContextsLabelText', 'Cluster Contexts') }).component(); - this.clusterContextsLabel.width = labelWidth; - this.errorLoadingClustersLabel = this.view.modelBuilder.text().withProperties({ value: localize('bdc-errorLoadingClustersText', 'No cluster information is found in the config file or an error ocurred while loading the config file') }).component(); - this.clusterContextList = this.view.modelBuilder.divContainer().component(); - this.clusterContextLoadingComponent = this.view.modelBuilder.loadingComponent().withItem(this.clusterContextList).component(); - this.existingClusterControl = this.view.modelBuilder.divContainer().component(); - let clusterContextContainer = this.view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', alignItems: 'start' }).component(); - clusterContextContainer.addItem(this.clusterContextsLabel, { flex: '0 0 auto' }); - clusterContextContainer.addItem(this.clusterContextLoadingComponent, { flex: '0 0 auto', CSSStyles: { 'width': '400px', 'margin-left': '10px', 'margin-top': '10px' } }); - - this.existingClusterControl.addItem(configFileContainer, { CSSStyles: { 'margin-top': '0px' } }); - this.existingClusterControl.addItem(clusterContextContainer, { - CSSStyles: { 'margin- top': '10px' } - }); - - this.wizard.registerDisposable(this.browseFileButton.onDidClick(async () => { - let fileUris = await vscode.window.showOpenDialog( - { - canSelectFiles: true, - canSelectFolders: false, - canSelectMany: false, - defaultUri: vscode.Uri.file(os.homedir()), - openLabel: localize('bdc-selectKubeConfigFileText', 'Select'), - filters: { - 'KubeConfig Files': ['*'], - } - } - ); - - if (!fileUris || fileUris.length === 0) { - return; - } - self.clusterContextList.clearItems(); - - let fileUri = fileUris[0]; - - self.loadClusterContexts(fileUri.fsPath); - })); - } - - private async loadClusterContexts(configPath: string): Promise { - this.clusterContextLoadingComponent.loading = true; - let self = this; - this.configFileInput.value = configPath; - await setActiveKubeconfig(configPath); - - let clusters = await this.wizard.model.loadClusters(); - if (clusters.length !== 0) { - let options = clusters.map(cluster => { - let option = this.view.modelBuilder.radioButton().withProperties({ - label: cluster.contextName, - checked: cluster.active, - name: ClusterRadioButtonGroupName - }).component(); - - if (cluster.active) { - self.wizard.model.selectedCluster = cluster; - self.wizard.wizardObject.message = null; - } - - this.wizard.registerDisposable(option.onDidClick(() => { - self.wizard.model.selectedCluster = cluster; - self.wizard.wizardObject.message = null; - })); - return option; - }); - self.clusterContextList.addItems(options); - } else { - self.clusterContextList.addItem(this.errorLoadingClustersLabel); - } - this.clusterContextLoadingComponent.loading = false; - } -} \ No newline at end of file diff --git a/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectTargetClusterTypePage.ts b/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectTargetClusterTypePage.ts deleted file mode 100644 index 0ae97e0067..0000000000 --- a/extensions/big-data-cluster/src/wizards/create-cluster/pages/selectTargetClusterTypePage.ts +++ /dev/null @@ -1,259 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as azdata from 'azdata'; -import { WizardPageBase } from '../../wizardPageBase'; -import { TargetClusterTypeInfo, ToolInstallationStatus, ToolInfo } from '../../../interfaces'; -import * as nls from 'vscode-nls'; -import { CreateClusterWizard } from '../createClusterWizard'; - -const localize = nls.loadMessageBundle(); - -const InstallToolsButtonText = localize('bdc-create.InstallToolsText', 'Install Tools'); -const InstallingButtonText = localize('bdc-create.InstallingButtonText', 'Installing...'); - -export class SelectTargetClusterTypePage extends WizardPageBase { - private cards: azdata.CardComponent[]; - private toolsTable: azdata.TableComponent; - private formBuilder: azdata.FormBuilder; - private form: azdata.FormContainer; - private installToolsButton: azdata.window.Button; - private toolsLoadingWrapper: azdata.LoadingComponent; - private refreshToolsButton: azdata.window.Button; - private targetDescriptionText: azdata.TextComponent; - private targetDescriptionGroup: azdata.FormComponent; - private isValid: boolean = false; - private isLoading: boolean = false; - private requiredTools: ToolInfo[]; - - constructor(wizard: CreateClusterWizard) { - super(localize('bdc-create.selectTargetClusterTypePageTitle', 'What is your target cluster environment?'), - localize('bdc-create.selectTargetClusterTypePageDescription', 'Choose the target environment and then install the required tools for it.'), - wizard); - this.installToolsButton = azdata.window.createButton(InstallToolsButtonText); - this.installToolsButton.hidden = true; - this.wizard.registerDisposable(this.installToolsButton.onClick(async () => { - this.wizard.wizardObject.message = null; - this.installToolsButton.label = InstallingButtonText; - this.installToolsButton.enabled = false; - this.refreshToolsButton.enabled = false; - if (this.requiredTools) { - for (let i = 0; i < this.requiredTools.length; i++) { - let tool = this.requiredTools[i]; - if (tool.status === ToolInstallationStatus.NotInstalled) { - tool.status = ToolInstallationStatus.Installing; - this.updateToolStatusTable(); - await this.wizard.model.installTool(tool); - } - } - } - - this.installToolsButton.label = InstallToolsButtonText; - this.updateRequiredToolStatus(); - })); - this.wizard.addButton(this.installToolsButton); - - this.refreshToolsButton = azdata.window.createButton(localize('bdc-create.RefreshToolsButtonText', 'Refresh Status')); - this.refreshToolsButton.hidden = true; - this.wizard.registerDisposable(this.refreshToolsButton.onClick(() => { - this.updateRequiredToolStatus(); - })); - this.wizard.addButton(this.refreshToolsButton); - } - - protected initialize(view: azdata.ModelView): Thenable { - let self = this; - self.registerNavigationValidator(); - return self.wizard.model.getAllTargetClusterTypeInfo().then((clusterTypes) => { - self.cards = []; - - clusterTypes.forEach(clusterType => { - let card = self.createCard(view, clusterType); - self.cards.push(card); - }); - let cardsContainer = view.modelBuilder.flexContainer().withItems(self.cards, { flex: '0 0 auto', CSSStyles: { 'margin-bottom': '20px' } }).withLayout({ flexFlow: 'row', alignItems: 'left' }).component(); - - self.targetDescriptionText = view.modelBuilder.text().component(); - - let toolColumn: azdata.TableColumn = { - value: localize('bdc-create.toolNameColumnHeader', 'Tool'), - width: 100 - }; - let descriptionColumn: azdata.TableColumn = { - value: localize('bdc-create.toolDescriptionColumnHeader', 'Description'), - width: 500 - }; - let versionColumn: azdata.TableColumn = { - value: localize('bdc-create.toolVersionColumnHeader', 'Version'), - width: 200 - }; - let statusColumn: azdata.TableColumn = { - value: localize('bdc-create.toolStatusColumnHeader', 'Status'), - width: 200 - }; - - self.toolsTable = view.modelBuilder.table().withProperties({ - height: 150, - data: [], - columns: [toolColumn, descriptionColumn, versionColumn, statusColumn], - width: 1000 - }).component(); - - self.toolsLoadingWrapper = view.modelBuilder.loadingComponent().withItem(self.toolsTable).component(); - self.formBuilder = view.modelBuilder.formContainer().withFormItems( - [ - { - component: cardsContainer, - title: localize('bdc-create.PickTargetEnvironmentText', 'Pick target environment') - } - ], - { - horizontal: false - } - ); - - self.form = self.formBuilder.withLayout({ width: '100%' }).component(); - return view.initializeModel(self.form); - }); - } - - public onEnter(): void { - this.installToolsButton.hidden = false; - this.refreshToolsButton.hidden = false; - this.refreshToolsButton.enabled = true; - this.installToolsButton.enabled = false; - this.registerNavigationValidator(); - } - - private registerNavigationValidator(): void { - this.wizard.wizardObject.registerNavigationValidator(() => { - if (this.isLoading) { - let messageText = localize('bdc-create.ToolsRefreshingText', 'Please wait while the required tools status is being refreshed.'); - let messageLevel = azdata.window.MessageLevel.Information; - this.wizard.wizardObject.message = { - level: messageLevel, - text: messageText - }; - return false; - } - if (!this.isValid) { - let messageText = this.cards.filter(c => { return c.selected; }).length === 0 ? - localize('bdc-create.TargetClusterTypeNotSelectedText', 'Please select a target cluster type.') : - localize('bdc-create.MissingToolsText', 'Please install the required tools.'); - this.wizard.wizardObject.message = { - level: azdata.window.MessageLevel.Error, - text: messageText - }; - } - return this.isValid; - }); - } - - public onLeave(): void { - this.installToolsButton.hidden = true; - this.refreshToolsButton.hidden = true; - } - - private createCard(view: azdata.ModelView, targetClusterTypeInfo: TargetClusterTypeInfo): azdata.CardComponent { - let self = this; - let descriptions = targetClusterTypeInfo.enabled ? [] : [localize('bdc-create.ComingSoonText', '(Coming Soon)')]; - let card = view.modelBuilder.card().withProperties({ - cardType: azdata.CardType.VerticalButton, - iconPath: { - dark: self.wizard.context.asAbsolutePath(targetClusterTypeInfo.iconPath.dark), - light: self.wizard.context.asAbsolutePath(targetClusterTypeInfo.iconPath.light) - }, - label: targetClusterTypeInfo.name, - descriptions: descriptions - }).component(); - - card.enabled = targetClusterTypeInfo.enabled; - - self.wizard.registerDisposable(card.onCardSelectedChanged(() => { - self.onCardSelected(card, targetClusterTypeInfo); - })); - return card; - } - - private onCardSelected(card: azdata.CardComponent, targetClusterTypeInfo: TargetClusterTypeInfo): void { - let self = this; - if (card.selected) { - self.wizard.wizardObject.message = null; - self.wizard.model.targetClusterType = targetClusterTypeInfo.type; - self.cards.forEach(c => { - if (c !== card) { - c.selected = false; - } - }); - - self.targetDescriptionText.value = targetClusterTypeInfo.description; - - if (self.form.items.length === 1) { - self.formBuilder.addFormItem({ - title: localize('bdc-create.RequiredToolsText', 'Required tools'), - component: self.toolsLoadingWrapper - }); - } else { - self.formBuilder.removeFormItem(self.targetDescriptionGroup); - } - - self.targetDescriptionGroup = { - title: targetClusterTypeInfo.fullName, - component: self.targetDescriptionText - }; - self.formBuilder.insertFormItem(self.targetDescriptionGroup, 1); - - self.updateRequiredToolStatus(); - } else { - if (self.cards.filter(c => { return c !== card && c.selected; }).length === 0) { - card.selected = true; - } - } - } - - private updateRequiredToolStatus(): Thenable { - this.isLoading = true; - this.installToolsButton.hidden = false; - this.refreshToolsButton.hidden = false; - this.toolsLoadingWrapper.loading = true; - this.refreshToolsButton.enabled = false; - this.installToolsButton.enabled = false; - return this.wizard.model.getRequiredToolStatus().then(tools => { - this.requiredTools = tools; - this.isLoading = false; - this.toolsLoadingWrapper.loading = false; - this.refreshToolsButton.enabled = true; - this.installToolsButton.enabled = tools.filter(tool => tool.status !== ToolInstallationStatus.Installed).length !== 0; - this.isValid = !this.installToolsButton.enabled; - this.wizard.wizardObject.message = null; - this.updateToolStatusTable(); - }); - } - - private getStatusText(status: ToolInstallationStatus): string { - switch (status) { - case ToolInstallationStatus.Installed: - return '✔️ ' + localize('bdc-create.InstalledText', 'Installed'); - case ToolInstallationStatus.NotInstalled: - return '❌ ' + localize('bdc-create.NotInstalledText', 'Not Installed'); - case ToolInstallationStatus.Installing: - return '⌛ ' + localize('bdc-create.InstallingText', 'Installing...'); - case ToolInstallationStatus.FailedToInstall: - return '❌ ' + localize('bdc-create.FailedToInstallText', 'Install Failed'); - default: - return 'unknown status'; - } - } - - private updateToolStatusTable(): void { - if (this.requiredTools) { - let tableData = this.requiredTools.map(tool => { - return [tool.name, tool.description, tool.version, this.getStatusText(tool.status)]; - }); - this.toolsTable.data = tableData; - } - } -} diff --git a/extensions/big-data-cluster/src/wizards/create-cluster/pages/settingsPage.ts b/extensions/big-data-cluster/src/wizards/create-cluster/pages/settingsPage.ts deleted file mode 100644 index e6dd775666..0000000000 --- a/extensions/big-data-cluster/src/wizards/create-cluster/pages/settingsPage.ts +++ /dev/null @@ -1,278 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as azdata from 'azdata'; -import { WizardPageBase } from '../../wizardPageBase'; -import * as nls from 'vscode-nls'; -import { ClusterPorts, ContainerRegistryInfo } from '../../../interfaces'; -import { CreateClusterWizard } from '../createClusterWizard'; - -const localize = nls.loadMessageBundle(); -const UserNameInputWidth = '300px'; -const PortInputWidth = '100px'; -const RestoreDefaultValuesText = localize('bdc-create.RestoreDefaultValuesText', 'Restore Default Values'); - -export class SettingsPage extends WizardPageBase { - private acceptEulaCheckbox: azdata.CheckBoxComponent; - - constructor(wizard: CreateClusterWizard) { - super(localize('bdc-create.settingsPageTitle', 'Settings'), - localize('bdc-create.settingsPageDescription', 'Configure the settings required for deploying SQL Server big data cluster'), - wizard); - } - - public onEnter(): void { - this.wizard.wizardObject.registerNavigationValidator((e) => { - if (e.lastPage > e.newPage) { - this.wizard.wizardObject.message = null; - return true; - } - if (!this.acceptEulaCheckbox.checked) { - this.wizard.wizardObject.message = { - text: localize('bdc-create.EulaNotAccepted', 'You need to accept the terms of services and privacy policy in order to proceed'), - level: azdata.window.MessageLevel.Error - }; - } else { - this.wizard.wizardObject.message = null; - } - return this.acceptEulaCheckbox.checked; - }); - } - - protected initialize(view: azdata.ModelView): Thenable { - let clusterPorts: ClusterPorts; - let containerRegistryInfo: ContainerRegistryInfo; - - let clusterPortsPromise = this.wizard.model.getDefaultPorts().then(ports => { - clusterPorts = ports; - }); - - let containerRegistryPromise = this.wizard.model.getDefaultContainerRegistryInfo().then(containerRegistry => { - containerRegistryInfo = containerRegistry; - }); - return Promise.all([clusterPortsPromise, containerRegistryPromise]).then(() => { - let formBuilder = view.modelBuilder.formContainer(); - - // User settings - let clusterNameInput = this.createInputWithLabel(view, { - label: localize('bdc-create.ClusterName', 'Cluster name'), - inputWidth: UserNameInputWidth, - isRequiredField: true - }, (input) => { - this.wizard.model.clusterName = input.value; - }); - - let adminUserNameInput = this.createInputWithLabel(view, { - label: localize('bdc-create.AdminUsernameText', 'Admin username'), - isRequiredField: true, - inputWidth: UserNameInputWidth - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.adminUserName = inputBox.value; - }); - let adminPasswordInput = this.createInputWithLabel(view, { - label: localize('bdc-create.AdminUserPasswordText', 'Password'), - isRequiredField: true, - inputType: 'password', - inputWidth: UserNameInputWidth - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.adminPassword = inputBox.value; - }); - - // Port settings - let sqlPortInput = this.createInputWithLabel(view, { - label: localize('bdc-create.SQLPortText', 'SQL Server master'), - isRequiredField: true, - inputWidth: PortInputWidth, - initialValue: clusterPorts.sql - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.sqlPort = inputBox.value; - }); - let knoxPortInput = this.createInputWithLabel(view, { - label: localize('bdc-create.KnoxPortText', 'Knox'), - isRequiredField: true, - inputWidth: PortInputWidth, - initialValue: clusterPorts.knox - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.knoxPort = inputBox.value; - }); - let controllerPortInput = this.createInputWithLabel(view, { - label: localize('bdc-create.ControllerPortText', 'Controller'), - isRequiredField: true, - inputWidth: PortInputWidth, - initialValue: clusterPorts.controller - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.controllerPort = inputBox.value; - }); - let proxyPortInput = this.createInputWithLabel(view, { - label: localize('bdc-create.ProxyPortText', 'Proxy'), - isRequiredField: true, - inputWidth: PortInputWidth, - initialValue: clusterPorts.proxy - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.proxyPort = inputBox.value; - }); - let grafanaPortInput = this.createInputWithLabel(view, { - label: localize('bdc-create.GrafanaPortText', 'Grafana dashboard'), - isRequiredField: true, - inputWidth: PortInputWidth, - initialValue: clusterPorts.grafana - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.grafanaPort = inputBox.value; - }); - let kibanaPortInput = this.createInputWithLabel(view, { - label: localize('bdc-create.KibanaPortText', 'Kibana dashboard'), - isRequiredField: true, - inputWidth: PortInputWidth, - initialValue: clusterPorts.kibana - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.kibanaPort = inputBox.value; - }); - let restorePortSettingsButton = view.modelBuilder.button().withProperties({ - label: RestoreDefaultValuesText, - width: 200 - }).component(); - this.wizard.registerDisposable(restorePortSettingsButton.onDidClick(() => { - sqlPortInput.input.value = clusterPorts.sql; - knoxPortInput.input.value = clusterPorts.knox; - controllerPortInput.input.value = clusterPorts.controller; - proxyPortInput.input.value = clusterPorts.proxy; - grafanaPortInput.input.value = clusterPorts.grafana; - kibanaPortInput.input.value = clusterPorts.kibana; - })); - - // Container Registry Settings - const registryUserNamePasswordHintText = localize('bdc-create.RegistryUserNamePasswordHintText', 'only required for private registries'); - let registryInput = this.createInputWithLabel(view, { - label: localize('bdc-create.RegistryText', 'Registry'), - isRequiredField: true, - inputWidth: UserNameInputWidth, - initialValue: containerRegistryInfo.registry - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.containerRegistry = inputBox.value; - }); - - let repositoryInput = this.createInputWithLabel(view, { - label: localize('bdc-create.RepositoryText', 'Repository'), - isRequiredField: true, - inputWidth: UserNameInputWidth, - initialValue: containerRegistryInfo.repository - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.containerRepository = inputBox.value; - }); - - let imageTagInput = this.createInputWithLabel(view, { - label: localize('bdc-create.ImageTagText', 'Image tag'), - isRequiredField: true, - inputWidth: UserNameInputWidth, - initialValue: containerRegistryInfo.imageTag - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.containerRegistry = inputBox.value; - }); - - let registryUserNameInput = this.createInputWithLabel(view, { - label: localize('bdc-create.RegistryUserNameText', 'Username'), - isRequiredField: false, - inputWidth: UserNameInputWidth, - placeHolder: registryUserNamePasswordHintText - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.containerRegistryUserName = inputBox.value; - }); - - let registryPasswordInput = this.createInputWithLabel(view, { - label: localize('bdc-create.RegistryPasswordText', 'Password'), - isRequiredField: false, - inputWidth: UserNameInputWidth, - placeHolder: registryUserNamePasswordHintText, - inputType: 'password' - }, (inputBox: azdata.InputBoxComponent) => { - this.wizard.model.containerRegistryPassword = inputBox.value; - }); - let restoreContainerSettingsButton = view.modelBuilder.button().withProperties({ - label: RestoreDefaultValuesText, - width: 200 - }).component(); - this.wizard.registerDisposable(restoreContainerSettingsButton.onDidClick(() => { - registryInput.input.value = containerRegistryInfo.registry; - repositoryInput.input.value = containerRegistryInfo.repository; - imageTagInput.input.value = containerRegistryInfo.imageTag; - })); - - let basicSettingsGroup = view.modelBuilder.groupContainer().withItems([clusterNameInput.row, adminUserNameInput.row, adminPasswordInput.row]).withLayout({ header: localize('bdc-create.BasicSettingsText', 'Basic Settings'), collapsible: true }).component(); - let containerSettingsGroup = view.modelBuilder.groupContainer().withItems([registryInput.row, repositoryInput.row, imageTagInput.row, registryUserNameInput.row, registryPasswordInput.row, restoreContainerSettingsButton]).withLayout({ header: localize('bdc-create.ContainerRegistrySettings', 'Container Registry Settings'), collapsible: true }).component(); - let portSettingsGroup = view.modelBuilder.groupContainer().withItems([sqlPortInput.row, knoxPortInput.row, controllerPortInput.row, proxyPortInput.row, grafanaPortInput.row, kibanaPortInput.row, restorePortSettingsButton]).withLayout({ header: localize('bdc-create.PortSettings', 'Port Settings (Optional)'), collapsible: true, collapsed: true }).component(); - - this.acceptEulaCheckbox = view.modelBuilder.checkBox().component(); - this.acceptEulaCheckbox.checked = false; - - let eulaLink: azdata.LinkArea = { - text: localize('bdc-create.LicenseTerms', 'license terms'), - url: 'https://go.microsoft.com/fwlink/?LinkId=2002534' - }; - let privacyPolicyLink: azdata.LinkArea = { - text: localize('bdc-create.PrivacyPolicyText', 'privacy policy'), - url: 'https://go.microsoft.com/fwlink/?LinkId=853010' - }; - - let checkboxText = view.modelBuilder.text().withProperties({ - value: localize({ - key: 'bdc-create.AcceptTermsText', - comment: ['{0} is the place holder for license terms, {1} is the place holder for privacy policy'] - }, 'I accept the {0} and {1}.'), - links: [eulaLink, privacyPolicyLink] - }).component(); - - let eulaContainer = this.createRow(view, [this.acceptEulaCheckbox, checkboxText]); - - let form = formBuilder.withFormItems([ - { - title: '', - component: eulaContainer - }, { - title: '', - component: basicSettingsGroup - }, { - title: '', - component: containerSettingsGroup - }, { - title: '', - component: portSettingsGroup - }]).component(); - return view.initializeModel(form); - }); - } - - private createInputWithLabel(view: azdata.ModelView, options: { - label: string, - isRequiredField: boolean, - inputWidth: string, - inputType?: string, - initialValue?: string, - placeHolder?: string - }, textChangedHandler: (inputBox: azdata.InputBoxComponent) => void): { row: azdata.FlexContainer, input: azdata.InputBoxComponent } { - let inputType = !!options.inputType ? options.inputType : 'text'; - let input = view.modelBuilder.inputBox().withProperties({ - required: options.isRequiredField, - inputType: inputType - }).component(); - let text = view.modelBuilder.text().withProperties({ value: options.label }).component(); - input.width = options.inputWidth; - text.width = '150px'; - input.placeHolder = options.placeHolder; - this.wizard.registerDisposable(input.onTextChanged(() => { - textChangedHandler(input); - })); - input.value = options.initialValue; - let row = this.createRow(view, [text, input]); - return { - input: input, - row: row - }; - } - - private createRow(view: azdata.ModelView, items: azdata.Component[]): azdata.FlexContainer { - return view.modelBuilder.flexContainer().withItems(items, { CSSStyles: { 'margin-right': '5px' } }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component(); - } -} diff --git a/extensions/big-data-cluster/src/wizards/create-cluster/pages/summaryPage.ts b/extensions/big-data-cluster/src/wizards/create-cluster/pages/summaryPage.ts deleted file mode 100644 index df1821ac58..0000000000 --- a/extensions/big-data-cluster/src/wizards/create-cluster/pages/summaryPage.ts +++ /dev/null @@ -1,103 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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 { WizardPageBase } from '../../wizardPageBase'; -import { CreateClusterWizard } from '../createClusterWizard'; -import * as nls from 'vscode-nls'; - -const localize = nls.loadMessageBundle(); -const LabelWidth = '250px'; - -export class SummaryPage extends WizardPageBase { - private view: azdata.ModelView; - private targetTypeText: azdata.TextComponent; - private targetClusterContextText: azdata.TextComponent; - private clusterNameText: azdata.TextComponent; - private clusterAdminUsernameText: azdata.TextComponent; - private acceptEulaText: azdata.TextComponent; - private deploymentProfileText: azdata.TextComponent; - private sqlServerMasterScaleText: azdata.TextComponent; - private storagePoolScaleText: azdata.TextComponent; - private computePoolScaleText: azdata.TextComponent; - private dataPoolScaleText: azdata.TextComponent; - private sparkPoolScaleText: azdata.TextComponent; - - constructor(wizard: CreateClusterWizard) { - super(localize('bdc-create.summaryPageTitle', 'Summary'), '', wizard); - } - - protected initialize(view: azdata.ModelView): Thenable { - this.view = view; - let targetClusterInfoGroup = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); - let bdcClusterInfoGroup = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); - this.targetTypeText = this.view.modelBuilder.text().component(); - this.targetClusterContextText = this.view.modelBuilder.text().component(); - this.clusterNameText = this.view.modelBuilder.text().component(); - this.clusterAdminUsernameText = this.view.modelBuilder.text().component(); - this.acceptEulaText = this.view.modelBuilder.text().component(); - this.deploymentProfileText = this.view.modelBuilder.text().component(); - this.sqlServerMasterScaleText = this.view.modelBuilder.text().component(); - this.storagePoolScaleText = this.view.modelBuilder.text().component(); - this.computePoolScaleText = this.view.modelBuilder.text().component(); - this.dataPoolScaleText = this.view.modelBuilder.text().component(); - this.sparkPoolScaleText = this.view.modelBuilder.text().component(); - targetClusterInfoGroup.addItem(this.createRow(localize('bdc-create.TargetClusterTypeText', 'Cluster type'), this.targetTypeText)); - targetClusterInfoGroup.addItem(this.createRow(localize('bdc-create.ClusterContextText', 'Cluster context'), this.targetClusterContextText)); - - bdcClusterInfoGroup.addItem(this.createRow(localize('bdc-create.ClusterNameText', 'Cluster name'), this.clusterNameText)); - bdcClusterInfoGroup.addItem(this.createRow(localize('bdc-create.ClusterAdminUsernameText', 'Cluster Admin username'), this.clusterAdminUsernameText)); - bdcClusterInfoGroup.addItem(this.createRow(localize('bdc-create.AcceptEulaText', 'Accept license agreement'), this.acceptEulaText)); - bdcClusterInfoGroup.addItem(this.createRow(localize('bdc-create.DeploymentProfileText', 'Deployment profile'), this.deploymentProfileText)); - bdcClusterInfoGroup.addItem(this.createRow(localize('bdc-create.SqlServerMasterScaleText', 'SQL Server master scale'), this.sqlServerMasterScaleText)); - bdcClusterInfoGroup.addItem(this.createRow(localize('bdc-create.ComputePoolScaleText', 'Compute pool scale'), this.computePoolScaleText)); - bdcClusterInfoGroup.addItem(this.createRow(localize('bdc-create.DataPoolScaleText', 'Data pool scale'), this.dataPoolScaleText)); - bdcClusterInfoGroup.addItem(this.createRow(localize('bdc-create.StoragePoolScaleText', 'Storage pool scale'), this.storagePoolScaleText)); - bdcClusterInfoGroup.addItem(this.createRow(localize('bdc-create.SparkPoolScaleText', 'Spark pool scale'), this.sparkPoolScaleText)); - - let formBuilder = view.modelBuilder.formContainer(); - let form = formBuilder.withFormItems([{ - title: localize('bdc-create.TargetClusterGroupTitle', 'TARGET CLUSTER'), - component: targetClusterInfoGroup - }, { - title: localize('bdc-create.BigDataClusterGroupTitle', 'SQL SERVER BIG DATA CLUSTER'), - component: bdcClusterInfoGroup - }]).component(); - - return view.initializeModel(form); - } - - public onEnter(): void { - this.wizard.model.getAllTargetClusterTypeInfo().then((clusterTypes) => { - let selectedClusterType = clusterTypes.filter(clusterType => clusterType.type === this.wizard.model.targetClusterType)[0]; - this.targetTypeText.value = selectedClusterType.fullName; - this.targetClusterContextText.value = this.wizard.model.selectedCluster.contextName; - this.clusterNameText.value = this.wizard.model.clusterName; - this.clusterAdminUsernameText.value = this.wizard.model.adminUserName; - this.acceptEulaText.value = localize('bdc-create.YesText', 'Yes'); - this.deploymentProfileText.value = this.wizard.model.profile.name; - this.sqlServerMasterScaleText.value = this.wizard.model.profile.sqlServerMasterConfiguration.scale.toString(); - this.computePoolScaleText.value = this.wizard.model.profile.computePoolConfiguration.scale.toString(); - this.dataPoolScaleText.value = this.wizard.model.profile.dataPoolConfiguration.scale.toString(); - this.storagePoolScaleText.value = this.wizard.model.profile.storagePoolConfiguration.scale.toString(); - this.sparkPoolScaleText.value = this.wizard.model.profile.sparkPoolConfiguration.scale.toString(); - - }); - this.wizard.wizardObject.generateScriptButton.hidden = false; - } - - public onLeave(): void { - this.wizard.wizardObject.generateScriptButton.hidden = true; - } - - private createRow(label: string, textComponent: azdata.TextComponent): azdata.FlexContainer { - let row = this.view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', alignItems: 'baseline' }).component(); - let labelComponent = this.view.modelBuilder.text().withProperties({ value: label }).component(); - labelComponent.width = LabelWidth; - textComponent.width = LabelWidth; - row.addItems([labelComponent, textComponent]); - return row; - } -} diff --git a/extensions/big-data-cluster/src/wizards/wizardBase.ts b/extensions/big-data-cluster/src/wizards/wizardBase.ts deleted file mode 100644 index 724329552d..0000000000 --- a/extensions/big-data-cluster/src/wizards/wizardBase.ts +++ /dev/null @@ -1,76 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as azdata from 'azdata'; -import { ExtensionContext, Disposable } from 'vscode'; -import { WizardPageBase } from './wizardPageBase'; - -export abstract class WizardBase { - - public wizardObject: azdata.window.Wizard; - private customButtons: azdata.window.Button[]; - private pages: WizardPageBase[]; - - private toDispose: Disposable[] = []; - - constructor(public model: T, public context: ExtensionContext, private title: string) { - this.customButtons = []; - } - - public open(): Thenable { - this.wizardObject = azdata.window.createWizard(this.title); - this.initialize(); - this.wizardObject.customButtons = this.customButtons; - this.toDispose.push(this.wizardObject.onPageChanged((e) => { - let previousPage = this.pages[e.lastPage]; - let newPage = this.pages[e.newPage]; - previousPage.onLeave(); - newPage.onEnter(); - })); - - this.toDispose.push(this.wizardObject.doneButton.onClick(() => { - this.onOk(); - this.dispose(); - })); - this.toDispose.push(this.wizardObject.cancelButton.onClick(() => { - this.onCancel(); - this.dispose(); - })); - - return this.wizardObject.open().then(() => { - if (this.pages && this.pages.length > 0) { - this.pages[0].onEnter(); - } - }); - - } - - protected abstract initialize(): void; - protected abstract onOk(): void; - protected abstract onCancel(): void; - - public addButton(button: azdata.window.Button) { - this.customButtons.push(button); - } - - protected setPages(pages: WizardPageBase[]) { - this.wizardObject.pages = pages.map(p => p.pageObject); - this.pages = pages; - } - - private dispose() { - this.toDispose.forEach((disposable: Disposable) => { - try { - disposable.dispose(); - } - catch{ } - }); - } - - public registerDisposable(disposable: Disposable): void { - this.toDispose.push(disposable); - } -} diff --git a/extensions/big-data-cluster/src/wizards/wizardPageBase.ts b/extensions/big-data-cluster/src/wizards/wizardPageBase.ts deleted file mode 100644 index 586d8b62ec..0000000000 --- a/extensions/big-data-cluster/src/wizards/wizardPageBase.ts +++ /dev/null @@ -1,33 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -'use strict'; - -import * as azdata from 'azdata'; - -export abstract class WizardPageBase { - private _page: azdata.window.WizardPage; - - public get pageObject(): azdata.window.WizardPage { - return this._page; - } - - public get wizard(): T { - return this._wizard; - } - - constructor(title: string, description: string, private _wizard: T) { - this._page = azdata.window.createWizardPage(title); - this._page.description = description; - this._page.registerContent((view: azdata.ModelView) => { - return this.initialize(view); - }); - } - - protected abstract initialize(view: azdata.ModelView): Thenable; - - public onEnter(): void { } - - public onLeave(): void { } -} diff --git a/extensions/big-data-cluster/tsconfig.json b/extensions/big-data-cluster/tsconfig.json index d9fbb41ead..723a00781a 100644 --- a/extensions/big-data-cluster/tsconfig.json +++ b/extensions/big-data-cluster/tsconfig.json @@ -1,15 +1,23 @@ { - "extends": "../shared.tsconfig.json", + "compileOnSave": true, "compilerOptions": { + "module": "commonjs", + "target": "es6", "outDir": "./out", - "strict": false, - "alwaysStrict": false, - "noImplicitAny": false, - "noImplicitReturns": false, - "noUnusedLocals": false, - "noUnusedParameters": false + "lib": [ + "es6", + "es2015.promise" + ], + "sourceMap": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "declaration": false, + "typeRoots": [ + "./node_modules/@types" + ] }, - "include": [ - "src/**/*" + "exclude": [ + "node_modules" ] -} +} \ No newline at end of file diff --git a/extensions/big-data-cluster/yarn.lock b/extensions/big-data-cluster/yarn.lock index 4050ca0812..448db25a99 100644 --- a/extensions/big-data-cluster/yarn.lock +++ b/extensions/big-data-cluster/yarn.lock @@ -2,28 +2,71 @@ # yarn lockfile v1 -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= +"@types/mocha@^5.2.5": + version "5.2.7" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea" + integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ== + +"@types/node@^8.0.24": + version "8.10.49" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.49.tgz#f331afc5efed0796798e5591d6e0ece636969b7b" + integrity sha512-YX30JVx0PvSmJ3Eqr74fYLGeBxD+C7vIL20ek+GGGLJeUbVYRUW3EzyAXpIRA0K8c8o0UWqR/GwEFYiFoz1T8w== + +agent-base@4, agent-base@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +ajv@^6.5.5: + version "6.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" + integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -base64-js@^1.0.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" - integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== - -bl@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" - integrity sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA== +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: - readable-stream "^2.3.5" - safe-buffer "^5.1.1" + tweetnacl "^0.14.3" brace-expansion@^1.1.7: version "1.1.11" @@ -33,93 +76,59 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -buffer-alloc-unsafe@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" - integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -buffer-alloc@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec" - integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +circular-json@^0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" + integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: - buffer-alloc-unsafe "^1.1.0" - buffer-fill "^1.0.0" + delayed-stream "~1.0.0" -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -buffer-fill@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" - integrity sha1-+PeLdniYiO858gXNY39o5wISKyw= - -buffer@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6" - integrity sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - -caw@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/caw/-/caw-2.0.1.tgz#6c3ca071fc194720883c2dc5da9b074bfc7e9e95" - integrity sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA== - dependencies: - get-proxy "^2.0.0" - isurl "^1.0.0-alpha5" - tunnel-agent "^0.6.0" - url-to-options "^1.0.1" - -charenc@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= - -commander@~2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.8.1.tgz#06be367febfda0c330aa1e2a072d3dc9762425d4" - integrity sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ= - dependencies: - graceful-readlink ">= 1.0.0" +commander@2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -config-chain@^1.1.11: - version "1.1.12" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -content-disposition@^0.5.2: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== - dependencies: - safe-buffer "5.1.2" - -core-util-is@~1.0.0: +core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -crypt@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" -debug@^2.2.0: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== dependencies: ms "2.0.0" @@ -130,185 +139,96 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -decompress-response@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= dependencies: - mimic-response "^1.0.0" + jsbn "~0.1.0" + safer-buffer "^2.1.0" -decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1" - integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= dependencies: - file-type "^5.2.0" - is-stream "^1.1.0" - tar-stream "^1.5.2" + es6-promise "^4.0.3" -decompress-tarbz2@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz#3082a5b880ea4043816349f378b56c516be1a39b" - integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== - dependencies: - decompress-tar "^4.1.0" - file-type "^6.1.0" - is-stream "^1.1.0" - seek-bzip "^1.0.5" - unbzip2-stream "^1.0.9" - -decompress-targz@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/decompress-targz/-/decompress-targz-4.1.1.tgz#c09bc35c4d11f3de09f2d2da53e9de23e7ce1eee" - integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== - dependencies: - decompress-tar "^4.1.1" - file-type "^5.2.0" - is-stream "^1.1.0" - -decompress-unzip@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/decompress-unzip/-/decompress-unzip-4.0.1.tgz#deaaccdfd14aeaf85578f733ae8210f9b4848f69" - integrity sha1-3qrM39FK6vhVePczroIQ+bSEj2k= - dependencies: - file-type "^3.8.0" - get-stream "^2.2.0" - pify "^2.3.0" - yauzl "^2.4.2" - -decompress@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/decompress/-/decompress-4.2.0.tgz#7aedd85427e5a92dacfe55674a7c505e96d01f9d" - integrity sha1-eu3YVCflqS2s/lVnSnxQXpbQH50= - dependencies: - decompress-tar "^4.0.0" - decompress-tarbz2 "^4.0.0" - decompress-targz "^4.0.0" - decompress-unzip "^4.0.1" - graceful-fs "^4.1.10" - make-dir "^1.0.0" - pify "^2.3.0" - strip-dirs "^2.0.0" - -download@^6.2.5: - version "6.2.5" - resolved "https://registry.yarnpkg.com/download/-/download-6.2.5.tgz#acd6a542e4cd0bb42ca70cfc98c9e43b07039714" - integrity sha512-DpO9K1sXAST8Cpzb7kmEhogJxymyVUd5qz/vCOSyvwtp2Klj2XcDt5YUuasgxka44SxF0q5RriKIwJmQHG2AuA== - dependencies: - caw "^2.0.0" - content-disposition "^0.5.2" - decompress "^4.0.0" - ext-name "^5.0.0" - file-type "5.2.0" - filenamify "^2.0.0" - get-stream "^3.0.0" - got "^7.0.0" - make-dir "^1.0.0" - p-event "^1.0.0" - pify "^3.0.0" - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -end-of-stream@^1.0.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" - integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== - dependencies: - once "^1.4.0" - -escape-string-regexp@^1.0.2: +escape-string-regexp@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -ext-list@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37" - integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== - dependencies: - mime-db "^1.28.0" +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -ext-name@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6" - integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== - dependencies: - ext-list "^2.0.0" - sort-keys-length "^1.0.0" +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -file-type@5.2.0, file-type@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-5.2.0.tgz#2ddbea7c73ffe36368dfae49dc338c058c2b8ad6" - integrity sha1-LdvqfHP/42No365J3DOMBYwritY= +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= -file-type@^3.8.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" - integrity sha1-JXoHg4TR24CHvESdEH1SpSZyuek= - -file-type@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/file-type/-/file-type-6.2.0.tgz#e50cd75d356ffed4e306dc4f5bcf52a79903a919" - integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== - -filename-reserved-regex@^2.0.0: +fast-json-stable-stringify@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" - integrity sha1-q/c9+rc10EVECr/qLZHzieu/oik= + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= -filenamify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-2.1.0.tgz#88faf495fb1b47abfd612300002a16228c677ee9" - integrity sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA== +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== dependencies: - filename-reserved-regex "^2.0.0" - strip-outer "^1.0.0" - trim-repeated "^1.0.0" - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -get-proxy@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/get-proxy/-/get-proxy-2.1.0.tgz#349f2b4d91d44c4d4d4e9cba2ad90143fac5ef93" - integrity sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw== +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= dependencies: - npm-conf "^1.1.0" + assert-plus "^1.0.0" -get-stream@^2.2.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-2.3.1.tgz#5f38f93f346009666ee0150a054167f91bdd95de" - integrity sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4= - dependencies: - object-assign "^4.0.1" - pinkie-promise "^2.0.0" - -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= - -glob@^7.0.0: - version "7.1.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" - integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== +glob@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -317,52 +237,70 @@ glob@^7.0.0: once "^1.3.0" path-is-absolute "^1.0.0" -got@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" - integrity sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw== +glob@^7.1.2: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== dependencies: - decompress-response "^3.2.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-plain-obj "^1.1.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^1.1.1" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - url-parse-lax "^1.0.0" - url-to-options "^1.0.1" + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" -graceful-fs@^4.1.10: - version "4.1.15" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" - integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" - integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" - integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== - -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" - integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== +har-validator@~5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== dependencies: - has-symbol-support-x "^1.4.1" + ajv "^6.5.5" + har-schema "^2.0.0" -ieee754@^1.1.4: - version "1.1.12" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" - integrity sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA== +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= + +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== + dependencies: + agent-base "4" + debug "3.1.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" + integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ== + dependencies: + agent-base "^4.1.0" + debug "^3.1.0" inflight@^1.0.4: version "1.0.6" @@ -372,101 +310,69 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@~2.0.3: +inherits@2: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== - -interpret@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" - integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== - -is-buffer@~1.1.1: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-natural-number@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" - integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= - -is-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" - integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= - -is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= - -is-retry-allowed@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" - integrity sha1-EaBgVotnM5REAz0BJaYaINVk+zQ= - -is-stream@^1.0.0, is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -isarray@~1.0.0: +is-typedarray@~1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" - integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" -lodash@^4.16.4: +lodash@^4.17.4: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== -lowercase-keys@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== +mime-db@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== -make-dir@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" - integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== dependencies: - pify "^3.0.0" + mime-db "1.40.0" -md5@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" - integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= - dependencies: - charenc "~0.0.1" - crypt "~0.0.1" - is-buffer "~1.1.1" - -mime-db@^1.28.0: - version "1.38.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" - integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== - -mimic-response@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -478,31 +384,29 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -mkdirp@~0.5.1: +mkdirp@0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" -mocha-junit-reporter@^1.17.0: - version "1.18.0" - resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.18.0.tgz#9209a3fba30025ae3ae5e6bfe7f9c5bc3c2e8ee2" - integrity sha512-y3XuqKa2+HRYtg0wYyhW/XsLm2Ps+pqf9HaTAt7+MVUAKFJaNAHOrNseTZo9KCxjfIbxUWwckP5qCDDPUmjSWA== +mocha@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" + integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== dependencies: - debug "^2.2.0" - md5 "^2.1.0" - mkdirp "~0.5.1" - strip-ansi "^4.0.0" - xml "^1.0.0" - -mocha-multi-reporters@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/mocha-multi-reporters/-/mocha-multi-reporters-1.1.7.tgz#cc7f3f4d32f478520941d852abb64d9988587d82" - integrity sha1-zH8/TTL0eFIJQdhSq7ZNmYhYfYI= - dependencies: - debug "^3.1.0" - lodash "^4.16.4" + browser-stdout "1.3.1" + commander "2.15.1" + debug "3.1.0" + diff "3.5.0" + escape-string-regexp "1.0.5" + glob "7.1.2" + growl "1.10.5" + he "1.1.1" + minimatch "3.0.4" + mkdirp "0.5.1" + supports-color "5.4.0" ms@2.0.0: version "2.0.0" @@ -510,230 +414,194 @@ ms@2.0.0: integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= ms@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -npm-conf@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -once@^1.3.0, once@^1.4.0: +once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= dependencies: wrappy "1" -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" - integrity sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw== - -p-event@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-event/-/p-event-1.3.0.tgz#8e6b4f4f65c72bc5b6fe28b75eda874f96a4a085" - integrity sha1-jmtPT2XHK8W2/ii3XtqHT5akoIU= - dependencies: - p-timeout "^1.1.1" - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-timeout@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" - integrity sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y= - dependencies: - p-finally "^1.0.0" - path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= +postinstall-build@^5.0.1: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postinstall-build/-/postinstall-build-5.0.3.tgz#238692f712a481d8f5bc8960e94786036241efc7" + integrity sha512-vPvPe8TKgp4FLgY3+DfxCE5PIfoXBK2lyLfNCxsRbDsV6vS4oU5RG/IWxrblMn6heagbnMED3MemUQllQ2bQUg== -pify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= +psl@^1.1.24: + version "1.1.32" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.32.tgz#3f132717cf2f9c169724b2b6caf373cf694198db" + integrity sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g== -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + +request@^2.88.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== dependencies: - pinkie "^2.0.0" + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= -prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= - -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== - -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - -readable-stream@^2.3.0, readable-stream@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - integrity sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= - dependencies: - resolve "^1.1.6" - -resolve@^1.1.6: - version "1.10.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" - integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg== - dependencies: - path-parse "^1.0.6" - -safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -seek-bzip@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc" - integrity sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w= +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^5.4.1: + version "5.7.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" + integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== + +should-equal@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-2.0.0.tgz#6072cf83047360867e68e98b09d71143d04ee0c3" + integrity sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA== dependencies: - commander "~2.8.1" + should-type "^1.4.0" -shelljs@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097" - integrity sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A== +should-format@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/should-format/-/should-format-3.0.3.tgz#9bfc8f74fa39205c53d38c34d717303e277124f1" + integrity sha1-m/yPdPo5IFxT04w01xcwPidxJPE= dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" + should-type "^1.3.0" + should-type-adaptors "^1.0.1" -sort-keys-length@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188" - integrity sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg= +should-type-adaptors@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz#401e7f33b5533033944d5cd8bf2b65027792e27a" + integrity sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA== dependencies: - sort-keys "^1.0.0" + should-type "^1.3.0" + should-util "^1.0.0" -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" - integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= - dependencies: - is-plain-obj "^1.0.0" +should-type@^1.3.0, should-type@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/should-type/-/should-type-1.4.0.tgz#0756d8ce846dfd09843a6947719dfa0d4cff5cf3" + integrity sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM= -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - -strip-dirs@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/strip-dirs/-/strip-dirs-2.1.0.tgz#4987736264fc344cf20f6c34aca9d13d1d4ed6c5" - integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== - dependencies: - is-natural-number "^4.0.1" - -strip-outer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" - integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== - dependencies: - escape-string-regexp "^1.0.2" - -tar-stream@^1.5.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" - integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== - dependencies: - bl "^1.0.0" - buffer-alloc "^1.2.0" - end-of-stream "^1.0.0" - fs-constants "^1.0.0" - readable-stream "^2.3.0" - to-buffer "^1.1.1" - xtend "^4.0.0" - -through@^2.3.8: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -timed-out@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= - -to-buffer@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" - integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== - -trim-repeated@^1.0.0: +should-util@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" - integrity sha1-42RqLqTokTEr9+rObPsFOAvAHCE= + resolved "https://registry.yarnpkg.com/should-util/-/should-util-1.0.0.tgz#c98cda374aa6b190df8ba87c9889c2b4db620063" + integrity sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM= + +should@^13.2.1: + version "13.2.3" + resolved "https://registry.yarnpkg.com/should/-/should-13.2.3.tgz#96d8e5acf3e97b49d89b51feaa5ae8d07ef58f10" + integrity sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ== dependencies: - escape-string-regexp "^1.0.2" + should-equal "^2.0.0" + should-format "^3.0.3" + should-type "^1.4.0" + should-type-adaptors "^1.0.1" + should-util "^1.0.0" + +source-map-support@^0.5.0: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +supports-color@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== + dependencies: + has-flag "^3.0.0" + +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== + dependencies: + psl "^1.1.24" + punycode "^1.4.1" tunnel-agent@^0.6.0: version "0.6.0" @@ -742,55 +610,76 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -unbzip2-stream@^1.0.9: - version "1.3.3" - resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.3.3.tgz#d156d205e670d8d8c393e1c02ebd506422873f6a" - integrity sha512-fUlAF7U9Ah1Q6EieQ4x4zLNejrRvDWUYmxXUpN3uziFYCHapjWFaCAnreY9bGgxzaMCFAPPpYNng57CypwJVhg== +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +typemoq@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/typemoq/-/typemoq-2.1.0.tgz#4452ce360d92cf2a1a180f0c29de2803f87af1e8" + integrity sha512-DtRNLb7x8yCTv/KHlwes+NI+aGb4Vl1iPC63Hhtcvk1DpxSAZzKWQv0RQFY0jX2Uqj0SDBNl8Na4e6MV6TNDgw== dependencies: - buffer "^5.2.1" - through "^2.3.8" + circular-json "^0.3.1" + lodash "^4.17.4" + postinstall-build "^5.0.1" -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - integrity sha1-evjzA2Rem9eaJy56FKxovAYJ2nM= +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== dependencies: - prepend-http "^1.0.1" + punycode "^2.1.0" -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" - integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= +url-parse@^1.4.4: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +uuid@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -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== +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +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" + resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8" + integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w== + dependencies: + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + +vscode@^1.1.26: + version "1.1.35" + resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.35.tgz#f8c6beb905738b874d539ddb3784e5feeec71c0a" + integrity sha512-xPnxzQU40LOS2yPyzWW+WKpTV6qA3z16TcgpZ9O38UWLA157Zz4GxUx5H7Gd07pxzw0GqvusbF4D+5GBgNxvEQ== + dependencies: + glob "^7.1.2" + mocha "^5.2.0" + request "^2.88.0" + semver "^5.4.1" + source-map-support "^0.5.0" + url-parse "^1.4.4" + vscode-test "^0.4.1" wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -xml@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" - integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= - -xtend@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= - -yauzl@^2.4.2: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0"