From bafd9fd437faac6a50c3a093be0634fdcf0cbd29 Mon Sep 17 00:00:00 2001 From: Arvind Ranasaria Date: Sun, 17 Nov 2019 21:39:57 -0800 Subject: [PATCH] Native installers for azdata in auto deployment (#8285) * code complete * minor fixes from self-review * installation searchaPaths and display logs fixes * revert inadvertent change * fixing installaton roott for debian and mac * Chaning from getos to linux-release-info with sync api usage for figuring out os distribution * adding file missed in previous commit * fixing indvertent compile error that creeped in * fix default install root for azli --- extensions/resource-deployment/package.json | 733 +++++++++--------- .../resource-deployment/src/interfaces.ts | 13 +- .../src/services/azdataService.ts | 5 - .../src/services/platformService.ts | 34 +- .../src/services/tools/azCliTool.ts | 33 +- .../src/services/tools/azdataTool.ts | 73 +- .../src/services/tools/dockerTool.ts | 4 +- .../src/services/tools/kubeCtlTool.ts | 28 +- .../src/services/tools/toolBase.ts | 22 +- .../src/typings/linuxReleaseInfo.d.ts | 28 + extensions/resource-deployment/yarn.lock | 5 + 11 files changed, 529 insertions(+), 449 deletions(-) create mode 100644 extensions/resource-deployment/src/typings/linuxReleaseInfo.d.ts diff --git a/extensions/resource-deployment/package.json b/extensions/resource-deployment/package.json index a1c500c30d..b25f2331d8 100644 --- a/extensions/resource-deployment/package.json +++ b/extensions/resource-deployment/package.json @@ -1,368 +1,369 @@ { - "name": "resource-deployment", - "displayName": "%extension-displayName%", - "description": "%extension-description%", - "version": "0.0.1", - "publisher": "Microsoft", - "preview": true, - "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt", - "icon": "images/sqlserver.png", - "aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e", - "engines": { - "vscode": "*", - "azdata": ">=1.6.0" - }, - "activationEvents": [ - "*" - ], - "main": "./out/main", - "repository": { - "type": "git", - "url": "https://github.com/Microsoft/azuredatastudio.git" - }, - "extensionDependencies": [ - "microsoft.mssql", - "microsoft.notebook" - ], - "contributes": { - "configuration": [ - { - "title": "%deployment.configuration.title%", - "properties": { - "deployment.azdataPipInstallUri": { - "type": "string", - "default": "https://aka.ms/azdata", - "description": "%azdata-pip-install-uri-description%" - }, - "deployment.azdataPipInstallArgs": { - "type": "string", - "default": "", - "description": "%azdata-pip-install-args-description%" - } - } - } - ], - "commands": [ - { - "command": "azdata.resource.deploy", - "title": "%deploy-resource-command-name%", - "category": "%deploy-resource-command-category%" - }, - { - "command": "azdata.openNotebookInputDialog", - "title": "Open notebook input dialog", - "category": "Notebook" - } - ], - "menus": { - "commandPalette": [ - { - "command": "azdata.openNotebookInputDialog", - "when": "false" - } - ], - "dataExplorer/action": [ - { - "command": "azdata.resource.deploy", - "group": "secondary" - } - ] - }, - "resourceDeploymentTypes": [ - { - "name": "sql-image", - "displayIndex": 2, - "displayName": "%resource-type-sql-image-display-name%", - "description": "%resource-type-sql-image-description%", - "platforms": "*", - "icon": { - "light": "./images/sql_server_container.svg", - "dark": "./images/sql_server_container_inverse.svg" - }, - "options": [ - { - "name": "version", - "displayName": "%version-display-name%", - "values": [ - { - "name": "sql2017", - "displayName": "%sql-2017-display-name%" - }, - { - "name": "sql2019", - "displayName": "%sql-2019-display-name%" - } - ] - } - ], - "providers": [ - { - "dialog": { - "notebook": "%sql-2017-docker-notebook%", - "title": "%docker-sql-2017-title%", - "name": "docker-sql-2017-dialog", - "tabs": [ - { - "title": "", - "sections": [ - { - "title": "", - "fields": [ - { - "label": "%docker-container-name-field%", - "variableName": "AZDATA_NB_VAR_DOCKER_CONTAINER_NAME", - "type": "datetime_text", - "defaultValue": "SQL2017-", - "required": true - }, - { - "label": "%docker-sql-password-field%", - "variableName": "AZDATA_NB_VAR_DOCKER_PASSWORD", - "type": "sql_password", - "userName": "sa", - "confirmationRequired": true, - "confirmationLabel": "%docker-confirm-sql-password-field%", - "defaultValue": "", - "required": true - }, - { - "label": "%docker-sql-port-field%", - "variableName": "AZDATA_NB_VAR_DOCKER_PORT", - "type": "number", - "defaultValue": "1433", - "required": true, - "min": 1, - "max": 65535 - } - ] - } - ] - } - ] - }, - "requiredTools": [ - { - "name": "docker" - } - ], - "when": "version=sql2017" - }, - { - "dialog": { - "notebook": "%sql-2019-docker-notebook%", - "title": "%docker-sql-2019-title%", - "name": "docker-sql-2019-dialog", - "tabs": [ - { - "title": "", - "sections": [ - { - "title": "", - "fields": [ - { - "label": "%docker-container-name-field%", - "variableName": "AZDATA_NB_VAR_DOCKER_CONTAINER_NAME", - "type": "datetime_text", - "defaultValue": "SQL2019-", - "required": true - }, - { - "label": "%docker-sql-password-field%", - "variableName": "AZDATA_NB_VAR_DOCKER_PASSWORD", - "type": "sql_password", - "userName": "sa", - "confirmationRequired": true, - "confirmationLabel": "%docker-confirm-sql-password-field%", - "defaultValue": "", - "required": true - }, - { - "label": "%docker-sql-port-field%", - "variableName": "AZDATA_NB_VAR_DOCKER_PORT", - "type": "number", - "defaultValue": "1433", - "required": true, - "min": 1, - "max": 65535 - } - ] - } - ] - } - ] - }, - "requiredTools": [ - { - "name": "docker" - } - ], - "when": "version=sql2019" - } - ] - }, - { - "name": "sql-bdc", - "displayIndex": 3, - "displayName": "%resource-type-sql-bdc-display-name%", - "description": "%resource-type-sql-bdc-description%", - "platforms": "*", - "icon": { - "light": "./images/sql_bdc.svg", - "dark": "./images/sql_bdc_inverse.svg" - }, - "options": [ - { - "name": "version", - "displayName": "%version-display-name%", - "values": [ - { - "name": "bdc2019", - "displayName": "%bdc-2019-display-name%" - } - ] - }, - { - "name": "target", - "displayName": "%bdc-deployment-target%", - "values": [ - { - "name": "new-aks", - "displayName": "%bdc-deployment-target-new-aks%" - }, - { - "name": "existing-aks", - "displayName": "%bdc-deployment-target-existing-aks%" - }, - { - "name": "existing-kubeadm", - "displayName": "%bdc-deployment-target-existing-kubeadm%" - } - ] - } - ], - "providers": [ - { - "wizard": { - "type": "new-aks", - "notebook": "%bdc-2019-aks-notebook%" - }, - "requiredTools": [ - { - "name": "kubectl" - }, - { - "name": "azure-cli" - }, - { - "name": "azdata", - "version": "15.0.2070" - } - ], - "when": "target=new-aks&&version=bdc2019" - }, - { - "wizard": { - "type": "existing-aks", - "notebook": "%bdc-2019-existing-aks-notebook%" - }, - "requiredTools": [ - { - "name": "kubectl" - }, - { - "name": "azdata" - } - ], - "when": "target=existing-aks&&version=bdc2019" - }, - { - "wizard": { - "type": "existing-kubeadm", - "notebook": "%bdc-2019-existing-kubeadm-notebook%" - }, - "requiredTools": [ - { - "name": "kubectl" - }, - { - "name": "azdata" - } - ], - "when": "target=existing-kubeadm&&version=bdc2019" - } - ], - "agreement": { - "template": "%bdc-agreement%", - "links": [ - { - "text": "%bdc-agreement-privacy-statement%", - "url": "https://go.microsoft.com/fwlink/?LinkId=853010" - }, - { - "text": "%bdc-agreement-bdc-eula%", - "url": "https://go.microsoft.com/fwlink/?LinkId=2002534" - }, - { - "text": "%bdc-agreement-azdata-eula%", - "url": "https://aka.ms/azdata-eula" - } - ] - } - }, - { - "name": "sql-windows-setup", - "displayIndex": 1, - "displayName": "%resource-type-sql-windows-setup-display-name%", - "description": "%resource-type-sql-windows-setup-description%", - "platforms": [ - "win32" - ], - "icon": { - "light": "./images/sql_server_on_windows.svg", - "dark": "./images/sql_server_on_windows_inverse.svg" - }, - "options": [ - { - "name": "version", - "displayName": "%version-display-name%", - "values": [ - { - "name": "sql2017", - "displayName": "%sql-2017-display-name%" - }, - { - "name": "sql2019", - "displayName": "%sql-2019-display-name%" - } - ] - } - ], - "providers": [ - { - "downloadUrl": "https://go.microsoft.com/fwlink/?linkid=853016", - "requiredTools": [], - "when": "version=sql2017" - }, - { - "webPageUrl": "https://www.microsoft.com/evalcenter/evaluate-sql-server-2019-rc", - "requiredTools": [], - "when": "version=sql2019" - } - ] - } - ] - }, - "dependencies": { - "promisify-child-process": "^3.1.1", - "sudo-prompt": "^9.0.0", - "vscode-nls": "^4.0.0", - "yamljs": "^0.3.0" - }, - "devDependencies": { - "@types/yamljs": "0.2.30", - "mocha-junit-reporter": "^1.17.0", - "mocha-multi-reporters": "^1.1.7", - "typemoq": "^2.1.0", - "vscode": "^1.1.26" - } + "name": "resource-deployment", + "displayName": "%extension-displayName%", + "description": "%extension-description%", + "version": "0.0.1", + "publisher": "Microsoft", + "preview": true, + "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt", + "icon": "images/sqlserver.png", + "aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e", + "engines": { + "vscode": "*", + "azdata": ">=1.6.0" + }, + "activationEvents": [ + "*" + ], + "main": "./out/main", + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/azuredatastudio.git" + }, + "extensionDependencies": [ + "microsoft.mssql", + "microsoft.notebook" + ], + "contributes": { + "configuration": [ + { + "title": "%deployment.configuration.title%", + "properties": { + "deployment.azdataPipInstallUri": { + "type": "string", + "default": "https://aka.ms/azdata", + "description": "%azdata-pip-install-uri-description%" + }, + "deployment.azdataPipInstallArgs": { + "type": "string", + "default": "", + "description": "%azdata-pip-install-args-description%" + } + } + } + ], + "commands": [ + { + "command": "azdata.resource.deploy", + "title": "%deploy-resource-command-name%", + "category": "%deploy-resource-command-category%" + }, + { + "command": "azdata.openNotebookInputDialog", + "title": "Open notebook input dialog", + "category": "Notebook" + } + ], + "menus": { + "commandPalette": [ + { + "command": "azdata.openNotebookInputDialog", + "when": "false" + } + ], + "dataExplorer/action": [ + { + "command": "azdata.resource.deploy", + "group": "secondary" + } + ] + }, + "resourceDeploymentTypes": [ + { + "name": "sql-image", + "displayIndex": 2, + "displayName": "%resource-type-sql-image-display-name%", + "description": "%resource-type-sql-image-description%", + "platforms": "*", + "icon": { + "light": "./images/sql_server_container.svg", + "dark": "./images/sql_server_container_inverse.svg" + }, + "options": [ + { + "name": "version", + "displayName": "%version-display-name%", + "values": [ + { + "name": "sql2017", + "displayName": "%sql-2017-display-name%" + }, + { + "name": "sql2019", + "displayName": "%sql-2019-display-name%" + } + ] + } + ], + "providers": [ + { + "dialog": { + "notebook": "%sql-2017-docker-notebook%", + "title": "%docker-sql-2017-title%", + "name": "docker-sql-2017-dialog", + "tabs": [ + { + "title": "", + "sections": [ + { + "title": "", + "fields": [ + { + "label": "%docker-container-name-field%", + "variableName": "AZDATA_NB_VAR_DOCKER_CONTAINER_NAME", + "type": "datetime_text", + "defaultValue": "SQL2017-", + "required": true + }, + { + "label": "%docker-sql-password-field%", + "variableName": "AZDATA_NB_VAR_DOCKER_PASSWORD", + "type": "sql_password", + "userName": "sa", + "confirmationRequired": true, + "confirmationLabel": "%docker-confirm-sql-password-field%", + "defaultValue": "", + "required": true + }, + { + "label": "%docker-sql-port-field%", + "variableName": "AZDATA_NB_VAR_DOCKER_PORT", + "type": "number", + "defaultValue": "1433", + "required": true, + "min": 1, + "max": 65535 + } + ] + } + ] + } + ] + }, + "requiredTools": [ + { + "name": "docker" + } + ], + "when": "version=sql2017" + }, + { + "dialog": { + "notebook": "%sql-2019-docker-notebook%", + "title": "%docker-sql-2019-title%", + "name": "docker-sql-2019-dialog", + "tabs": [ + { + "title": "", + "sections": [ + { + "title": "", + "fields": [ + { + "label": "%docker-container-name-field%", + "variableName": "AZDATA_NB_VAR_DOCKER_CONTAINER_NAME", + "type": "datetime_text", + "defaultValue": "SQL2019-", + "required": true + }, + { + "label": "%docker-sql-password-field%", + "variableName": "AZDATA_NB_VAR_DOCKER_PASSWORD", + "type": "sql_password", + "userName": "sa", + "confirmationRequired": true, + "confirmationLabel": "%docker-confirm-sql-password-field%", + "defaultValue": "", + "required": true + }, + { + "label": "%docker-sql-port-field%", + "variableName": "AZDATA_NB_VAR_DOCKER_PORT", + "type": "number", + "defaultValue": "1433", + "required": true, + "min": 1, + "max": 65535 + } + ] + } + ] + } + ] + }, + "requiredTools": [ + { + "name": "docker" + } + ], + "when": "version=sql2019" + } + ] + }, + { + "name": "sql-bdc", + "displayIndex": 3, + "displayName": "%resource-type-sql-bdc-display-name%", + "description": "%resource-type-sql-bdc-description%", + "platforms": "*", + "icon": { + "light": "./images/sql_bdc.svg", + "dark": "./images/sql_bdc_inverse.svg" + }, + "options": [ + { + "name": "version", + "displayName": "%version-display-name%", + "values": [ + { + "name": "bdc2019", + "displayName": "%bdc-2019-display-name%" + } + ] + }, + { + "name": "target", + "displayName": "%bdc-deployment-target%", + "values": [ + { + "name": "new-aks", + "displayName": "%bdc-deployment-target-new-aks%" + }, + { + "name": "existing-aks", + "displayName": "%bdc-deployment-target-existing-aks%" + }, + { + "name": "existing-kubeadm", + "displayName": "%bdc-deployment-target-existing-kubeadm%" + } + ] + } + ], + "providers": [ + { + "wizard": { + "type": "new-aks", + "notebook": "%bdc-2019-aks-notebook%" + }, + "requiredTools": [ + { + "name": "kubectl" + }, + { + "name": "azure-cli" + }, + { + "name": "azdata", + "version": "15.0.2070" + } + ], + "when": "target=new-aks&&version=bdc2019" + }, + { + "wizard": { + "type": "existing-aks", + "notebook": "%bdc-2019-existing-aks-notebook%" + }, + "requiredTools": [ + { + "name": "kubectl" + }, + { + "name": "azdata" + } + ], + "when": "target=existing-aks&&version=bdc2019" + }, + { + "wizard": { + "type": "existing-kubeadm", + "notebook": "%bdc-2019-existing-kubeadm-notebook%" + }, + "requiredTools": [ + { + "name": "kubectl" + }, + { + "name": "azdata" + } + ], + "when": "target=existing-kubeadm&&version=bdc2019" + } + ], + "agreement": { + "template": "%bdc-agreement%", + "links": [ + { + "text": "%bdc-agreement-privacy-statement%", + "url": "https://go.microsoft.com/fwlink/?LinkId=853010" + }, + { + "text": "%bdc-agreement-bdc-eula%", + "url": "https://go.microsoft.com/fwlink/?LinkId=2002534" + }, + { + "text": "%bdc-agreement-azdata-eula%", + "url": "https://aka.ms/azdata-eula" + } + ] + } + }, + { + "name": "sql-windows-setup", + "displayIndex": 1, + "displayName": "%resource-type-sql-windows-setup-display-name%", + "description": "%resource-type-sql-windows-setup-description%", + "platforms": [ + "win32" + ], + "icon": { + "light": "./images/sql_server_on_windows.svg", + "dark": "./images/sql_server_on_windows_inverse.svg" + }, + "options": [ + { + "name": "version", + "displayName": "%version-display-name%", + "values": [ + { + "name": "sql2017", + "displayName": "%sql-2017-display-name%" + }, + { + "name": "sql2019", + "displayName": "%sql-2019-display-name%" + } + ] + } + ], + "providers": [ + { + "downloadUrl": "https://go.microsoft.com/fwlink/?linkid=853016", + "requiredTools": [], + "when": "version=sql2017" + }, + { + "webPageUrl": "https://www.microsoft.com/evalcenter/evaluate-sql-server-2019-rc", + "requiredTools": [], + "when": "version=sql2019" + } + ] + } + ] + }, + "dependencies": { + "linux-release-info": "^2.0.0", + "promisify-child-process": "^3.1.1", + "sudo-prompt": "^9.0.0", + "vscode-nls": "^4.0.0", + "yamljs": "^0.3.0" + }, + "devDependencies": { + "@types/yamljs": "0.2.30", + "mocha-junit-reporter": "^1.17.0", + "mocha-multi-reporters": "^1.1.7", + "typemoq": "^2.1.0", + "vscode": "^1.1.26" + } } diff --git a/extensions/resource-deployment/src/interfaces.ts b/extensions/resource-deployment/src/interfaces.ts index 7775e341a0..ef126b1fa7 100644 --- a/extensions/resource-deployment/src/interfaces.ts +++ b/extensions/resource-deployment/src/interfaces.ts @@ -201,12 +201,21 @@ export interface NotebookInfo { linux: string; } -export enum OsType { +export enum OsDistribution { win32 = 'win32', darwin = 'darwin', - linux = 'linux', + debian = 'debian', others = 'others' } +export interface OsRelease extends JSON { + type: string; + platform: string; + hostname: string; + arch: string; + release: string; + id?: string; + id_like?: string; +} export interface ToolRequirementInfo { name: string; diff --git a/extensions/resource-deployment/src/services/azdataService.ts b/extensions/resource-deployment/src/services/azdataService.ts index cbbb663085..eeb11794d3 100644 --- a/extensions/resource-deployment/src/services/azdataService.ts +++ b/extensions/resource-deployment/src/services/azdataService.ts @@ -38,7 +38,6 @@ export class AzdataService implements IAzdataService { default: throw new Error(`Unknown deployment type: ${deploymentType}`); } - await this.ensureWorkingDirectoryExists(); const profileNames = await this.getDeploymentProfileNames(); return await Promise.all(profileNames.filter(profile => profile.startsWith(profilePrefix)).map(profile => this.getDeploymentProfileInfo(profile))); } @@ -65,10 +64,6 @@ export class AzdataService implements IAzdataService { return new BigDataClusterDeploymentProfile(profileName, configObjects[0], configObjects[1]); } - private async ensureWorkingDirectoryExists(): Promise { - await this.platformService.ensureDirectoryExists(this.platformService.storagePath()); - } - private async getJsonObjectFromFile(path: string): Promise { return JSON.parse(await this.platformService.readTextFile(path)); } diff --git a/extensions/resource-deployment/src/services/platformService.ts b/extensions/resource-deployment/src/services/platformService.ts index 85886b6979..8d42dfb955 100644 --- a/extensions/resource-deployment/src/services/platformService.ts +++ b/extensions/resource-deployment/src/services/platformService.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; import * as fs from 'fs'; +import * as releaseInfo from 'linux-release-info'; import * as cp from 'promisify-child-process'; import * as sudo from 'sudo-prompt'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import { OsType } from '../interfaces'; +import { OsDistribution, OsRelease } from '../interfaces'; import { getErrorMessage } from '../utils'; const localize = nls.loadMessageBundle(); @@ -18,7 +19,7 @@ const sudoPromptTitle = 'AzureDataStudio'; * Abstract of platform dependencies */ export interface IPlatformService { - osType(): OsType; + osDistribution(): OsDistribution; platform(): string; storagePath(): string; initialize(): Promise; @@ -31,7 +32,6 @@ export interface IPlatformService { showOutputChannel(preserveFocus?: boolean): void; isNotebookNameUsed(title: string): boolean; makeDirectory(path: string): Promise; - ensureDirectoryExists(directory: string): Promise; readTextFile(filePath: string): Promise; runCommand(command: string, options?: CommandOptions): Promise; saveTextFile(content: string, path: string): Promise; @@ -57,14 +57,16 @@ export interface CommandOptions { export class PlatformService implements IPlatformService { private _outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel(extensionOutputChannel); - private _storagePathEnsurer: Promise; + private _initializationEnsurer: Promise; + private _osDistribution: OsDistribution; - constructor(private _storagePath: string = '') { - this._storagePathEnsurer = this.ensureDirectoryExists(_storagePath); + constructor(private _storagePath: string) { + this._osDistribution = this.getOsDistribution(); + this._initializationEnsurer = this.ensureDirectoryExists(_storagePath); } async initialize(): Promise { - await this._storagePathEnsurer; + await this._initializationEnsurer; } storagePath(): string { @@ -83,12 +85,18 @@ export class PlatformService implements IPlatformService { this._outputChannel.show(preserveFocus); } - osType(platform: string = this.platform()): OsType { - if (Object.values(OsType).includes(platform)) { - return platform; - } else { - return OsType.others; + osDistribution(): OsDistribution { + return this._osDistribution; + } + + private getOsDistribution(): OsDistribution { + const currentReleaseInfo = releaseInfo({ mode: 'sync' }); + const dist = currentReleaseInfo.id_like || currentReleaseInfo.id || currentReleaseInfo.platform; + if (Object.values(OsDistribution).includes(dist)) { + return dist; } + // all other unrecognized oses/distributions are treated as OsType.others + return OsDistribution.others; } async copyFile(source: string, target: string): Promise { @@ -127,7 +135,7 @@ export class PlatformService implements IPlatformService { *This function ensures that the given {@link directory} does not exist it creates it. It creates only the most leaf folder so if any ancestor folders are missing then this command throws an error. * @param directory - the path to ensure */ - async ensureDirectoryExists(directory: string): Promise { + private async ensureDirectoryExists(directory: string): Promise { if (!await this.fileExists(directory)) { await this.makeDirectory(directory); } diff --git a/extensions/resource-deployment/src/services/tools/azCliTool.ts b/extensions/resource-deployment/src/services/tools/azCliTool.ts index 70284f260e..cdf89ef79e 100644 --- a/extensions/resource-deployment/src/services/tools/azCliTool.ts +++ b/extensions/resource-deployment/src/services/tools/azCliTool.ts @@ -5,12 +5,12 @@ import { EOL } from 'os'; import { SemVer } from 'semver'; import * as nls from 'vscode-nls'; -import { Command, OsType, ToolType } from '../../interfaces'; +import { Command, OsDistribution, ToolType } from '../../interfaces'; import { IPlatformService } from '../platformService'; import { dependencyType, ToolBase } from './toolBase'; const localize = nls.loadMessageBundle(); -const defaultInstallationRoot = '~/.local/bin'; +const defaultInstallationRoot = '/usr/local/bin'; const win32InstallationRoot = `${process.env['ProgramFiles(x86)']}\\Microsoft SDKs\\Azure\\CLI2\\wbin`; export const AzCliToolName = 'azure-cli'; @@ -44,19 +44,19 @@ export class AzCliTool extends ToolBase { } protected async getSearchPaths(): Promise { - switch (this.osType) { - case OsType.win32: + switch (this.osDistribution) { + case OsDistribution.win32: return [win32InstallationRoot]; default: return [defaultInstallationRoot]; } } - protected readonly allInstallationCommands: Map = new Map([ - [OsType.linux, linuxInstallationCommands], - [OsType.win32, win32InstallationCommands], - [OsType.darwin, macOsInstallationCommands], - [OsType.others, defaultInstallationCommands] + protected readonly allInstallationCommands: Map = new Map([ + [OsDistribution.debian, debianInstallationCommands], + [OsDistribution.win32, win32InstallationCommands], + [OsDistribution.darwin, macOsInstallationCommands], + [OsDistribution.others, defaultInstallationCommands] ]); protected getVersionFromOutput(output: string): SemVer | undefined { @@ -73,11 +73,11 @@ export class AzCliTool extends ToolBase { }; } - protected dependenciesByOsType: Map = new Map([ - [OsType.linux, []], - [OsType.win32, []], - [OsType.darwin, [dependencyType.Brew]], - [OsType.others, [dependencyType.Curl]] + protected dependenciesByOsType: Map = new Map([ + [OsDistribution.debian, []], + [OsDistribution.win32, []], + [OsDistribution.darwin, [dependencyType.Brew]], + [OsDistribution.others, [dependencyType.Curl]] ]); protected get discoveryCommand(): Command { @@ -99,12 +99,11 @@ const win32InstallationCommands = [ }, { comment: localize('resourceDeployment.AziCli.DisplayingInstallationLog', "displaying the installation log …"), - command: `type AzureCliInstall.log | findstr /i /v /c:"cached product context" | findstr /i /v /c:"has no eligible binary patches" `, + command: `type ADS_AzureCliInstall.log | findstr /i /v "^MSI"`, ignoreError: true } ]; const macOsInstallationCommands = [ - // try to install brew ourselves { comment: localize('resourceDeployment.AziCli.UpdatingBrewRepository', "updating your brew repository for azure-cli installation …"), command: 'brew update' @@ -114,7 +113,7 @@ const macOsInstallationCommands = [ command: 'brew install azure-cli' } ]; -const linuxInstallationCommands = [ +const debianInstallationCommands = [ { sudo: true, comment: localize('resourceDeployment.AziCli.AptGetUpdate', "updating repository information before installing azure-cli …"), diff --git a/extensions/resource-deployment/src/services/tools/azdataTool.ts b/extensions/resource-deployment/src/services/tools/azdataTool.ts index 9769b10b5a..9138d48a71 100644 --- a/extensions/resource-deployment/src/services/tools/azdataTool.ts +++ b/extensions/resource-deployment/src/services/tools/azdataTool.ts @@ -8,12 +8,15 @@ import { SemVer } from 'semver'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { azdataPipInstallArgsKey, AzdataPipInstallUriKey, DeploymentConfigurationKey } from '../../constants'; -import { Command, OsType, ToolType } from '../../interfaces'; +import { Command, OsDistribution, ToolType } from '../../interfaces'; import { IPlatformService } from '../platformService'; import { dependencyType, ToolBase } from './toolBase'; const localize = nls.loadMessageBundle(); export const AzdataToolName = 'azdata'; +const win32InstallationRoot = `${process.env['ProgramFiles(x86)']}\\Microsoft SDKs\\Azdata\\CLI\\wbin`; +const macInstallationRoot = '/usr/local/bin'; +const debianInstallationRoot = '/usr/local/bin'; export class AzdataTool extends ToolBase { constructor(platformService: IPlatformService) { @@ -65,7 +68,13 @@ export class AzdataTool extends ToolBase { } protected async getSearchPaths(): Promise { - switch (this.osType) { + switch (this.osDistribution) { + case OsDistribution.win32: + return [win32InstallationRoot]; + case OsDistribution.darwin: + return [macInstallationRoot]; + case OsDistribution.debian: + return [debianInstallationRoot]; default: const azdataCliInstallLocation = await this.getPip3InstallLocation('azdata-cli'); if (azdataCliInstallLocation) { @@ -76,12 +85,12 @@ export class AzdataTool extends ToolBase { } } - protected get allInstallationCommands(): Map { - return new Map([ - [OsType.linux, this.defaultInstallationCommands], - [OsType.win32, this.defaultInstallationCommands], - [OsType.darwin, this.defaultInstallationCommands], - [OsType.others, this.defaultInstallationCommands] + protected get allInstallationCommands(): Map { + return new Map([ + [OsDistribution.debian, debianInstallationCommands], + [OsDistribution.win32, win32InstallationCommands], + [OsDistribution.darwin, macOsInstallationCommands], + [OsDistribution.others, this.defaultInstallationCommands] ]); } @@ -114,16 +123,45 @@ export class AzdataTool extends ToolBase { return vscode.workspace.getConfiguration(DeploymentConfigurationKey)[azdataPipInstallArgsKey]; } - protected dependenciesByOsType: Map = new Map([ - [OsType.linux, [dependencyType.PythonAndPip3]], - [OsType.win32, [dependencyType.PythonAndPip3]], - [OsType.darwin, [dependencyType.PythonAndPip3]], - [OsType.others, [dependencyType.PythonAndPip3]] + protected dependenciesByOsType: Map = new Map([ + [OsDistribution.debian, []], + [OsDistribution.win32, []], + [OsDistribution.darwin, []], + [OsDistribution.others, [dependencyType.PythonAndPip3]] ]); } -/* -const linuxInstallationCommands = [ +const win32InstallationCommands = [ + { + comment: localize('resourceDeployment.Azdata.DeletingPreviousAzdata.msi', "deleting previously downloaded Azdata.msi if one exists …"), + command: `IF EXIST .\\Azdata.msi DEL /F .\\Azdata.msi` + }, + { + sudo: true, + comment: localize('resourceDeployment.Azdata.DownloadingAndInstallingAzdata', "downloading Azdata.msi and installing azdata-cli …"), + command: `powershell -Command "& {(New-Object System.Net.WebClient).DownloadFile('https://aka.ms/azdata-msi', 'Azdata.msi'); Start-Process msiexec.exe -Wait -ArgumentList '/I Azdata.msi /passive /quiet /lvx ADS_AzdataInstall.log'}"` + }, + { + comment: localize('resourceDeployment.Azdata.DisplayingInstallationLog', "displaying the installation log …"), + command: `type ADS_AzdataInstall.log | findstr /i /v ^MSI"`, + ignoreError: true + } +]; +const macOsInstallationCommands = [ + { + comment: localize('resourceDeployment.Azdata.TappingBrewRepository', "tapping into the brew repository for azdata-cli …"), + command: 'brew tap microsoft/azdata-cli-release' + }, + { + comment: localize('resourceDeployment.Azdata.UpdatingBrewRepository', "updating the brew repository for azdata-cli installation …"), + command: 'brew update' + }, + { + comment: localize('resourceDeployment.Azdata.InstallingAzdata', "installing azdata …"), + command: 'brew install azdata-cli' + } +]; +const debianInstallationCommands = [ { sudo: true, comment: localize('resourceDeployment.Azdata.AptGetUpdate', "updating repository information …"), @@ -141,8 +179,8 @@ const linuxInstallationCommands = [ }, { sudo: true, - comment: localize('resourceDeployment.Azdata.AddingAzureCliRepositoryInformation', "adding the azdata repository information …"), - command: 'add-apt-repository "$(wget -qO- https://packages.microsoft.com/config/ubuntu/16.04/mssql-server-preview.list)"' + comment: localize('resourceDeployment.Azdata.AddingAzdataRepositoryInformation', "adding the azdata repository information …"), + command: 'add-apt-repository "$(wget -qO- https://packages.microsoft.com/config/ubuntu/16.04/mssql-server-2019.list)"' }, { sudo: true, @@ -155,4 +193,3 @@ const linuxInstallationCommands = [ command: 'apt-get install -y azdata-cli' } ]; -*/ diff --git a/extensions/resource-deployment/src/services/tools/dockerTool.ts b/extensions/resource-deployment/src/services/tools/dockerTool.ts index 9ef811a3db..f3ab868107 100644 --- a/extensions/resource-deployment/src/services/tools/dockerTool.ts +++ b/extensions/resource-deployment/src/services/tools/dockerTool.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { SemVer } from 'semver'; import * as nls from 'vscode-nls'; -import { Command, ToolType, OsType } from '../../interfaces'; +import { Command, ToolType, OsDistribution } from '../../interfaces'; import { IPlatformService } from '../platformService'; import { ToolBase } from './toolBase'; @@ -51,7 +51,7 @@ export class DockerTool extends ToolBase { return false; } - protected get allInstallationCommands(): Map { + protected get allInstallationCommands(): Map { throw Error('Installation of DockerTool is not supported'); } } diff --git a/extensions/resource-deployment/src/services/tools/kubeCtlTool.ts b/extensions/resource-deployment/src/services/tools/kubeCtlTool.ts index f3281bf9aa..4a43eac8fb 100644 --- a/extensions/resource-deployment/src/services/tools/kubeCtlTool.ts +++ b/extensions/resource-deployment/src/services/tools/kubeCtlTool.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Command, ToolType, OsType } from '../../interfaces'; +import { Command, ToolType, OsDistribution } from '../../interfaces'; import * as nls from 'vscode-nls'; import { SemVer } from 'semver'; import { IPlatformService } from '../platformService'; @@ -63,25 +63,25 @@ export class KubeCtlTool extends ToolBase { } protected async getSearchPaths(): Promise { - switch (this.osType) { - case OsType.win32: + switch (this.osDistribution) { + case OsDistribution.win32: return [this.storagePath]; default: return [defaultInstallationRoot]; } } - protected readonly allInstallationCommands: Map = new Map([ - [OsType.linux, linuxInstallationCommands], - [OsType.win32, win32InstallationCommands], - [OsType.darwin, macOsInstallationCommands], - [OsType.others, defaultInstallationCommands] + protected readonly allInstallationCommands: Map = new Map([ + [OsDistribution.debian, debianInstallationCommands], + [OsDistribution.win32, win32InstallationCommands], + [OsDistribution.darwin, macOsInstallationCommands], + [OsDistribution.others, defaultInstallationCommands] ]); - protected dependenciesByOsType: Map = new Map([ - [OsType.linux, []], - [OsType.win32, []], - [OsType.darwin, [dependencyType.Brew]], - [OsType.others, [dependencyType.Curl]] + protected dependenciesByOsType: Map = new Map([ + [OsDistribution.debian, []], + [OsDistribution.win32, []], + [OsDistribution.darwin, [dependencyType.Brew]], + [OsDistribution.others, [dependencyType.Curl]] ]); } @@ -95,7 +95,7 @@ const macOsInstallationCommands = [ command: 'brew install kubectl' } ]; -const linuxInstallationCommands = [ +const debianInstallationCommands = [ { sudo: true, comment: localize('resourceDeployment.Kubectl.AptGetUpdate', "updating repository information …"), diff --git a/extensions/resource-deployment/src/services/tools/toolBase.ts b/extensions/resource-deployment/src/services/tools/toolBase.ts index 08fc68141c..88bbc0c8e5 100644 --- a/extensions/resource-deployment/src/services/tools/toolBase.ts +++ b/extensions/resource-deployment/src/services/tools/toolBase.ts @@ -7,7 +7,7 @@ import * as path from 'path'; import { SemVer, compare } from 'semver'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; -import { Command, ITool, OsType, ToolStatus, ToolType } from '../../interfaces'; +import { Command, ITool, OsDistribution, ToolStatus, ToolType } from '../../interfaces'; import { getErrorMessage } from '../../utils'; import { IPlatformService } from '../platformService'; @@ -44,7 +44,6 @@ export const messageByDependencyType: Map = new Map; - protected readonly dependenciesByOsType: Map = new Map(); + protected abstract readonly allInstallationCommands: Map; + protected readonly dependenciesByOsType: Map = new Map(); protected abstract getVersionFromOutput(output: string): SemVer | undefined; protected readonly _onDidUpdateData = new vscode.EventEmitter(); @@ -63,7 +62,7 @@ export abstract class ToolBase implements ITool { protected abstract readonly versionCommand: Command; public get dependencyMessages(): string[] { - return (this.dependenciesByOsType.get(this.osType) || []).map((msgType: dependencyType) => messageByDependencyType.get(msgType)!); + return (this.dependenciesByOsType.get(this.osDistribution) || []).map((msgType: dependencyType) => messageByDependencyType.get(msgType)!); } protected async getInstallationPath(): Promise { @@ -125,8 +124,8 @@ export abstract class ToolBase implements ITool { return this._platformService.storagePath(); } - public get osType(): OsType { - return this._osType; + public get osDistribution(): OsDistribution { + return this._platformService.osDistribution(); } protected get version(): SemVer | undefined { @@ -152,7 +151,7 @@ export abstract class ToolBase implements ITool { } protected get installationCommands(): Command[] | undefined { - return this.allInstallationCommands.get(this.osType); + return this.allInstallationCommands.get(this.osDistribution); } protected async getPip3InstallLocation(packageName: string): Promise { @@ -272,10 +271,10 @@ export abstract class ToolBase implements ITool { } protected discoveryCommandString(toolBinary: string) { - switch (this.osType) { - case OsType.win32: + switch (this.osDistribution) { + case OsDistribution.win32: return `where.exe ${toolBinary}`; - case OsType.darwin: + case OsDistribution.darwin: return `command -v ${toolBinary}`; default: return `which ${toolBinary}`; @@ -304,7 +303,6 @@ export abstract class ToolBase implements ITool { } private _status: ToolStatus = ToolStatus.NotInstalled; - private _osType: OsType; private _version?: SemVer; private _statusDescription?: string; private _installationPath!: string; diff --git a/extensions/resource-deployment/src/typings/linuxReleaseInfo.d.ts b/extensions/resource-deployment/src/typings/linuxReleaseInfo.d.ts new file mode 100644 index 0000000000..a0c26d1346 --- /dev/null +++ b/extensions/resource-deployment/src/typings/linuxReleaseInfo.d.ts @@ -0,0 +1,28 @@ +declare module 'linux-release-info' { + namespace linuxReleaseInfo { + /** + * Get Os release info (distribution name, version, arch, release, etc.) from '/etc/os-release' or '/usr/lib/os-release' files and from native os module. On Windows and Darwin platforms it only returns common node os module info (platform, hostname, release, and arch) + * {@param options} - input options of type {@link OsReleaseOptions} - allows user to request sync/async and point to custom file for discovery of release information on linux distributions. If absent, default is async call with no custom file specified. + * (@param debug) - whether the api call should spew debug information to the console. Default is false. + * {@link https://github.com/samuelcarreira/linux-release-info/blob/master/README.md } + */ + interface OsReleaseInfoApi { + (options?: OsReleaseOptions, debug?: boolean): JSON | Promise; + } + + export interface OsReleaseOptions { + /** + * api calling mode: async/sync - default is 'async' + */ + mode?: 'async'|'sync'; + /** + * path to custom file on the linux os that can be used to discover the distribution information + */ + customReleaseInfoFile?: string; + + } + + } + const osReleaseInfo: linuxReleaseInfo.OsReleaseInfoApi; + export = osReleaseInfo; +} diff --git a/extensions/resource-deployment/yarn.lock b/extensions/resource-deployment/yarn.lock index e56af6096f..b00b46b394 100644 --- a/extensions/resource-deployment/yarn.lock +++ b/extensions/resource-deployment/yarn.lock @@ -398,6 +398,11 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +linux-release-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/linux-release-info/-/linux-release-info-2.0.0.tgz#bdd4743a3ac49151ce612dbfe063f5eec116aeab" + integrity sha512-w0RoUAZOQvnwuypQT+kwiDRNrMrEyrNWC8OOQH4e0Chii/BqB2tq7yeUHKo9brPDymY0Iz7EKe0TtwsflAlOnA== + lodash@^4.16.4, lodash@^4.17.4: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"