From a61b85c9ffd3e981c09b75cb12522be02f7d2fbc Mon Sep 17 00:00:00 2001 From: Charles Gagnon Date: Wed, 22 Jul 2020 13:41:52 -0700 Subject: [PATCH] Adds support for installing azdata on Linux (#11469) --- extensions/azdata/package.json | 1 + extensions/azdata/src/azdata.ts | 22 +++++++++++++---- extensions/azdata/src/common/childProcess.ts | 25 +++++++++++++++++++- extensions/azdata/src/extension.ts | 2 +- extensions/azdata/yarn.lock | 5 ++++ 5 files changed, 49 insertions(+), 6 deletions(-) diff --git a/extensions/azdata/package.json b/extensions/azdata/package.json index 0ce2c30fba..9711ed0851 100644 --- a/extensions/azdata/package.json +++ b/extensions/azdata/package.json @@ -21,6 +21,7 @@ "main": "./out/extension", "dependencies": { "request": "^2.88.2", + "sudo-prompt": "^9.2.1", "uuid": "^8.2.0", "vscode-nls": "^4.1.2", "which": "^2.0.2" diff --git a/extensions/azdata/src/azdata.ts b/extensions/azdata/src/azdata.ts index 48636d9863..954917b4f4 100644 --- a/extensions/azdata/src/azdata.ts +++ b/extensions/azdata/src/azdata.ts @@ -9,7 +9,7 @@ import * as uuid from 'uuid'; import * as vscode from 'vscode'; import { HttpClient } from './common/httpClient'; import * as loc from './localizedConstants'; -import { executeCommand } from './common/childProcess'; +import { executeCommand, executeSudoCommand } from './common/childProcess'; import { searchForCmd } from './common/utils'; export const azdataHostname = 'https://aka.ms'; @@ -55,6 +55,7 @@ export async function findAzdata(outputChannel: vscode.OutputChannel): Promise { const statusDisposable = vscode.window.setStatusBarMessage(loc.installingAzdata); + outputChannel.appendLine(loc.installingAzdata); try { switch (process.platform) { case 'win32': @@ -64,8 +65,10 @@ export async function downloadAndInstallAzdata(outputChannel: vscode.OutputChann await installAzdataDarwin(); break; case 'linux': - await installAzdataLinux(); + await installAzdataLinux(outputChannel); break; + default: + throw new Error(`Platform ${process.platform} is unsupported`); } } finally { statusDisposable.dispose(); @@ -93,8 +96,19 @@ async function installAzdataDarwin(): Promise { /** * Runs commands to install azdata on Linux */ -async function installAzdataLinux(): Promise { - throw new Error('Not yet implemented'); +async function installAzdataLinux(outputChannel: vscode.OutputChannel): Promise { + // https://docs.microsoft.com/en-us/sql/big-data-cluster/deploy-install-azdata-linux-package + // Get packages needed for install process + await executeSudoCommand('apt-get update', outputChannel); + await executeSudoCommand('apt-get install gnupg ca-certificates curl wget software-properties-common apt-transport-https lsb-release -y', outputChannel); + // Download and install the signing key + await executeSudoCommand('curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc.gpg > /dev/null', outputChannel); + // Add the azdata repository information + const release = (await executeCommand('lsb_release', ['-rs'], outputChannel)).trim(); + await executeSudoCommand(`add-apt-repository "$(wget -qO- https://packages.microsoft.com/config/ubuntu/${release}/mssql-server-2019.list)"`, outputChannel); + // Update repository information and install azdata + await executeSudoCommand('apt-get update', outputChannel); + await executeSudoCommand('apt-get install -y azdata-cli', outputChannel); } /** diff --git a/extensions/azdata/src/common/childProcess.ts b/extensions/azdata/src/common/childProcess.ts index 174f2f131f..0162c89a0b 100644 --- a/extensions/azdata/src/common/childProcess.ts +++ b/extensions/azdata/src/common/childProcess.ts @@ -5,6 +5,7 @@ import * as vscode from 'vscode'; import * as cp from 'child_process'; +import * as sudo from 'sudo-prompt'; import * as loc from '../localizedConstants'; /** @@ -17,7 +18,7 @@ export class ExitCodeError extends Error { } /** - * + * Executes the specified command. Throws an error for a non-0 exit code or if stderr receives output * @param command The command to execute * @param args Optional args to pass, every arg and arg value must be a separate item in the array * @param outputChannel Channel used to display diagnostic information @@ -42,3 +43,25 @@ export async function executeCommand(command: string, args?: string[], outputCha }); }); } + +/** + * Executes a command with admin privileges. The user will be prompted to enter credentials for invocation of + * this function. The exact prompt is platform-dependent. + * @param command The command to execute + * @param args The additional args + * @param outputChannel Channel used to display diagnostic information + */ +export async function executeSudoCommand(command: string, outputChannel?: vscode.OutputChannel): Promise { + return new Promise((resolve, reject) => { + outputChannel?.appendLine(loc.executingCommand(`sudo ${command}`, [])); + sudo.exec(command, { name: vscode.env.appName }, (error, stdout, stderr) => { + if (error) { + reject(error); + } else if (stderr) { + reject(stderr.toString('utf8')); + } else { + resolve(stdout ? stdout.toString('utf8') : ''); + } + }); + }); +} diff --git a/extensions/azdata/src/extension.ts b/extensions/azdata/src/extension.ts index 6b435638c8..9851fcc561 100644 --- a/extensions/azdata/src/extension.ts +++ b/extensions/azdata/src/extension.ts @@ -26,7 +26,7 @@ async function checkForAzdata(outputChannel: vscode.OutputChannel): Promise