mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Added in auto-install prompt for Azure CLI and arcdata extension, "Don't Ask Again" logic, removed waiting on azcli extension activate (#16646)
* Added back Don't Ask Again logic * If no Azure CLI found, throw error instead of returning undefined. * Deleted 'restart ADS' text for arcdata extension prompts * Added error catch for parse version and parsed out the * in az --version * Added back findAz() * Added arcdata version to AzTool. Parse --version using regex. * Return undefined if no az found. * Added userRequested param for findAz * No longer await on extension activate. Re-added some functions for az install. * Install works for windows * Changed auto install for az on Linux and MacOS. * Added comment for findSpecificAzAndArc and uncommented some localizedConstants * Added comment for getSemVersionArc and took out the path for some tests. * Made findSpecificAzAndArc return an object instead of a list * Removed azToolService test * Removed azToolService tests and renamed suite to azcli Extension Tests * Got rid of new Regexp for regex in parseVersions * Added back azToolService.ts * Added logic to enable prompt user to install arcdata extension and auto-install capability. No update capability yet. Co-authored-by: Candice Ye <canye@microsoft.com>
This commit is contained in:
@@ -32,6 +32,45 @@
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%azcli.arc.config.debug%"
|
||||
},
|
||||
"azcli.arcdataInstall": {
|
||||
"type": "string",
|
||||
"default": "prompt",
|
||||
"enum": [
|
||||
"dontPrompt",
|
||||
"prompt"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"%azcli.arc.install.dontPrompt.description%",
|
||||
"%azcli.arc.install.prompt.description%"
|
||||
],
|
||||
"description": "%azcli.arc.azArcdataInstallKey.description%"
|
||||
},
|
||||
"azcli.arcdataUpdate": {
|
||||
"type": "string",
|
||||
"default": "prompt",
|
||||
"enum": [
|
||||
"dontPrompt",
|
||||
"prompt"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"%azcli.arc.update.dontPrompt.description%",
|
||||
"%azcli.arc.update.prompt.description%"
|
||||
],
|
||||
"description": "%azcli.arc.azArcdataUpdateKey.description%"
|
||||
},
|
||||
"azcli.azInstall": {
|
||||
"type": "string",
|
||||
"default": "prompt",
|
||||
"enum": [
|
||||
"dontPrompt",
|
||||
"prompt"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"%azcli.install.dontPrompt.description%",
|
||||
"%azcli.install.prompt.description%"
|
||||
],
|
||||
"description": "%azcli.azCliInstallKey.description%"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,18 @@
|
||||
"azcli.arc.config.debug": "Log debug info to the output channel for all executed az commands",
|
||||
|
||||
"command.category": "Azure CLI",
|
||||
"azcli.arc.category": "Azure CLI"
|
||||
"azcli.arc.category": "Azure CLI",
|
||||
|
||||
"azcli.arc.azArcdataInstallKey.description": "Choose whether you will be prompted to download the Azure CLI arcdata extension.",
|
||||
"azcli.arc.install.prompt.description": "The user will be prompted to install the Azure CLI arcdata extension",
|
||||
"azcli.arc.install.dontPrompt.description": "The user will not be prompted to install the Azure CLI arcdata extension",
|
||||
|
||||
"azcli.arc.azArcdataUpdateKey.description": "Choose whether you will be prompted when an update of the Azure CLI arcdata extension is available.",
|
||||
"azcli.arc.update.prompt.description": "The user will be prompted for update of the Azure CLI arcdata extension",
|
||||
"azcli.arc.update.dontPrompt.description": "The user will not be prompted for update of the Azure CLI arcdata extension",
|
||||
|
||||
"azcli.azCliInstallKey.description": "Choose whether you will be prompted to install Azure CLI.",
|
||||
"azcli.install.prompt.description": "The user will be prompted to install the Azure CLI",
|
||||
"azcli.install.dontPrompt.description": "The user will not be prompted to install the Azure CLI"
|
||||
|
||||
}
|
||||
|
||||
@@ -26,28 +26,31 @@ export function throwIfNoAz(localAz: IAzTool | undefined): asserts localAz {
|
||||
}
|
||||
}
|
||||
|
||||
export function getExtensionApi(azToolService: AzToolService): azExt.IExtension {
|
||||
export function getExtensionApi(azToolService: AzToolService, localAzDiscovered: Promise<IAzTool | undefined>): azExt.IExtension {
|
||||
return {
|
||||
az: getAzApi(azToolService)
|
||||
az: getAzApi(localAzDiscovered, azToolService)
|
||||
};
|
||||
}
|
||||
|
||||
export function getAzApi(azToolService: AzToolService): azExt.IAzApi {
|
||||
export function getAzApi(localAzDiscovered: Promise<IAzTool | undefined>, azToolService: AzToolService): azExt.IAzApi {
|
||||
return {
|
||||
arcdata: {
|
||||
dc: {
|
||||
endpoint: {
|
||||
list: async (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||
await localAzDiscovered;
|
||||
validateAz(azToolService.localAz);
|
||||
return azToolService.localAz!.arcdata.dc.endpoint.list(namespace, additionalEnvVars);
|
||||
}
|
||||
},
|
||||
config: {
|
||||
list: async (additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||
await localAzDiscovered;
|
||||
validateAz(azToolService.localAz);
|
||||
return azToolService.localAz!.arcdata.dc.config.list(additionalEnvVars);
|
||||
},
|
||||
show: async (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||
await localAzDiscovered;
|
||||
validateAz(azToolService.localAz);
|
||||
return azToolService.localAz!.arcdata.dc.config.show(namespace, additionalEnvVars);
|
||||
}
|
||||
@@ -57,14 +60,17 @@ export function getAzApi(azToolService: AzToolService): azExt.IAzApi {
|
||||
postgres: {
|
||||
arcserver: {
|
||||
delete: async (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||
await localAzDiscovered;
|
||||
validateAz(azToolService.localAz);
|
||||
return azToolService.localAz!.postgres.arcserver.delete(name, namespace, additionalEnvVars);
|
||||
},
|
||||
list: async (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||
await localAzDiscovered;
|
||||
validateAz(azToolService.localAz);
|
||||
return azToolService.localAz!.postgres.arcserver.list(namespace, additionalEnvVars);
|
||||
},
|
||||
show: async (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||
await localAzDiscovered;
|
||||
validateAz(azToolService.localAz);
|
||||
return azToolService.localAz!.postgres.arcserver.show(name, namespace, additionalEnvVars);
|
||||
},
|
||||
@@ -87,6 +93,7 @@ export function getAzApi(azToolService: AzToolService): azExt.IAzApi {
|
||||
},
|
||||
namespace: string,
|
||||
additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||
await localAzDiscovered;
|
||||
validateAz(azToolService.localAz);
|
||||
return azToolService.localAz!.postgres.arcserver.edit(name, args, namespace, additionalEnvVars);
|
||||
}
|
||||
@@ -95,14 +102,17 @@ export function getAzApi(azToolService: AzToolService): azExt.IAzApi {
|
||||
sql: {
|
||||
miarc: {
|
||||
delete: async (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||
await localAzDiscovered;
|
||||
validateAz(azToolService.localAz);
|
||||
return azToolService.localAz!.sql.miarc.delete(name, namespace, additionalEnvVars);
|
||||
},
|
||||
list: async (namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||
await localAzDiscovered;
|
||||
validateAz(azToolService.localAz);
|
||||
return azToolService.localAz!.sql.miarc.list(namespace, additionalEnvVars);
|
||||
},
|
||||
show: async (name: string, namespace: string, additionalEnvVars?: azExt.AdditionalEnvVars) => {
|
||||
await localAzDiscovered;
|
||||
validateAz(azToolService.localAz);
|
||||
return azToolService.localAz!.sql.miarc.show(name, namespace, additionalEnvVars);
|
||||
},
|
||||
@@ -118,20 +128,29 @@ export function getAzApi(azToolService: AzToolService): azExt.IAzApi {
|
||||
namespace: string,
|
||||
additionalEnvVars?: azExt.AdditionalEnvVars
|
||||
) => {
|
||||
await localAzDiscovered;
|
||||
validateAz(azToolService.localAz);
|
||||
return azToolService.localAz!.sql.miarc.edit(name, args, namespace, additionalEnvVars);
|
||||
}
|
||||
}
|
||||
},
|
||||
getPath: async () => {
|
||||
await localAzDiscovered;
|
||||
throwIfNoAz(azToolService.localAz);
|
||||
return azToolService.localAz.getPath();
|
||||
},
|
||||
getSemVersion: async () => {
|
||||
getSemVersionAz: async () => {
|
||||
await localAzDiscovered;
|
||||
throwIfNoAz(azToolService.localAz);
|
||||
return azToolService.localAz.getSemVersion();
|
||||
return azToolService.localAz.getSemVersionAz();
|
||||
},
|
||||
getSemVersionArc: async () => {
|
||||
await localAzDiscovered;
|
||||
throwIfNoAz(azToolService.localAz);
|
||||
return azToolService.localAz.getSemVersionArc();
|
||||
},
|
||||
version: async () => {
|
||||
await localAzDiscovered;
|
||||
throwIfNoAz(azToolService.localAz);
|
||||
return azToolService.localAz.version();
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
import * as azExt from 'az-ext';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { SemVer } from 'semver';
|
||||
import * as vscode from 'vscode';
|
||||
import { executeCommand, ExitCodeError, ProcessOutput } from './common/childProcess';
|
||||
import { executeCommand, executeSudoCommand, ExitCodeError, ProcessOutput } from './common/childProcess';
|
||||
import { HttpClient } from './common/httpClient';
|
||||
import Logger from './common/logger';
|
||||
import { NoAzureCLIError, searchForCmd } from './common/utils';
|
||||
import { azConfigSection, azFound, debugConfigKey, latestAzArcExtensionVersion } from './constants';
|
||||
import { AzureCLIArcExtError, NoAzureCLIError, searchForCmd } from './common/utils';
|
||||
import { azArcdataInstallKey, azConfigSection, azFound, debugConfigKey, latestAzArcExtensionVersion, azCliInstallKey, azArcFound, azHostname, azUri } from './constants';
|
||||
import * as loc from './localizedConstants';
|
||||
|
||||
/**
|
||||
@@ -41,19 +43,30 @@ export interface IAzTool extends azExt.IAzApi {
|
||||
*/
|
||||
export class AzTool implements azExt.IAzApi {
|
||||
|
||||
private _semVersion: SemVer;
|
||||
private _semVersionAz: SemVer;
|
||||
private _semVersionArc: SemVer;
|
||||
|
||||
constructor(private _path: string, version: string) {
|
||||
this._semVersion = new SemVer(version);
|
||||
constructor(private _path: string, versionAz: string, versionArc: string) {
|
||||
this._semVersionAz = new SemVer(versionAz);
|
||||
this._semVersionArc = new SemVer(versionArc);
|
||||
}
|
||||
|
||||
/**
|
||||
* The semVersion corresponding to this installation of az. version() method should have been run
|
||||
* The semVersion corresponding to this installation of Azure CLI. version() method should have been run
|
||||
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
|
||||
* Az has gotten reinstalled in the background after this IAzApi object was constructed.
|
||||
*/
|
||||
public async getSemVersion(): Promise<SemVer> {
|
||||
return this._semVersion;
|
||||
public async getSemVersionAz(): Promise<SemVer> {
|
||||
return this._semVersionAz;
|
||||
}
|
||||
|
||||
/**
|
||||
* The semVersion corresponding to this installation of Azure CLI arcdata extension. version() method should have been run
|
||||
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
|
||||
* arcdata has gotten reinstalled in the background after this IAzApi object was constructed.
|
||||
*/
|
||||
public async getSemVersionArc(): Promise<SemVer> {
|
||||
return this._semVersionArc;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,7 +183,7 @@ export class AzTool implements azExt.IAzApi {
|
||||
*/
|
||||
public async version(): Promise<azExt.AzOutput<string>> {
|
||||
const output = await executeAzCommand(`"${this._path}"`, ['--version']);
|
||||
this._semVersion = new SemVer(parseVersion(output.stdout));
|
||||
this._semVersionAz = new SemVer(<string>parseVersion(output.stdout));
|
||||
return {
|
||||
stdout: output.stdout,
|
||||
stderr: output.stderr.split(os.EOL)
|
||||
@@ -218,44 +231,271 @@ export class AzTool implements azExt.IAzApi {
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns the existing installation of Azure CLI, or throws an error if it can't find it
|
||||
* or encountered an unexpected error.
|
||||
* The promise is rejected when Azure CLI is not found.
|
||||
* Checks whether az is installed - and if it is not then invokes the process of az installation.
|
||||
* @param userRequested true means that this operation by was requested by a user by executing an ads command.
|
||||
*/
|
||||
export async function findAz(): Promise<IAzTool> {
|
||||
export async function checkAndInstallAz(userRequested: boolean = false): Promise<IAzTool | undefined> {
|
||||
try {
|
||||
return await findAzAndArc(); // find currently installed Az
|
||||
} catch (err) {
|
||||
if (err === AzureCLIArcExtError) {
|
||||
// Az found but arcdata extension not found. Prompt user to install it, then check again.
|
||||
if (await promptToInstallArcdata(userRequested)) {
|
||||
return await findAzAndArc();
|
||||
}
|
||||
} else {
|
||||
// No az was found. Prompt user to install it, then check again.
|
||||
if (await promptToInstallAz(userRequested)) {
|
||||
return await findAzAndArc();
|
||||
}
|
||||
}
|
||||
}
|
||||
// If user declines to install upon prompt, return an undefined object instead of an AzTool
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the existing installation of az, or throws an error if it couldn't find it
|
||||
* or encountered an unexpected error. If arcdata extension was not found on the az,
|
||||
* throw an error. An AzTool will not be returned.
|
||||
* The promise is rejected when Az is not found.
|
||||
*/
|
||||
export async function findAzAndArc(): Promise<IAzTool> {
|
||||
Logger.log(loc.searchingForAz);
|
||||
try {
|
||||
const az = await findSpecificAz();
|
||||
Logger.log(loc.foundExistingAz(await az.getPath(), (await az.getSemVersion()).raw));
|
||||
return az;
|
||||
const azTool = await findSpecificAzAndArc();
|
||||
await vscode.commands.executeCommand('setContext', azFound, true); // save a context key that az was found so that command for installing az is no longer available in commandPalette and that for updating it is.
|
||||
await vscode.commands.executeCommand('setContext', azArcFound, true); // save a context key that arcdata was found so that command for installing arcdata is no longer available in commandPalette and that for updating it is.
|
||||
Logger.log(loc.foundExistingAz(await azTool.getPath(), (await azTool.getSemVersionAz()).raw, (await azTool.getSemVersionArc()).raw));
|
||||
return azTool;
|
||||
} catch (err) {
|
||||
Logger.log(loc.noAzureCLI);
|
||||
if (err === AzureCLIArcExtError) {
|
||||
Logger.log(loc.couldNotFindAzArc(err));
|
||||
Logger.log(loc.noAzArc);
|
||||
await vscode.commands.executeCommand('setContext', azArcFound, false); // save a context key that az was not found so that command for installing az is available in commandPalette and that for updating it is no longer available.
|
||||
} else {
|
||||
Logger.log(loc.couldNotFindAz(err));
|
||||
Logger.log(loc.noAz);
|
||||
await vscode.commands.executeCommand('setContext', azFound, false); // save a context key that arcdata was not found so that command for installing arcdata is available in commandPalette and that for updating it is no longer available.
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find az by searching user's directories. If no az is found, this will error out and no arcdata is found.
|
||||
* If az is found, check if arcdata extension exists on it and return true if so, false if not.
|
||||
* Return the AzTool whether or not an arcdata extension has been found.
|
||||
*/
|
||||
async function findSpecificAzAndArc(): Promise<IAzTool> {
|
||||
// Check if az exists
|
||||
const path = await ((process.platform === 'win32') ? searchForCmd('az.cmd') : searchForCmd('az'));
|
||||
const versionOutput = await executeAzCommand(`"${path}"`, ['--version']);
|
||||
|
||||
// The arcdata extension can't exist if there is no az. The function will not reach the following code
|
||||
// if no az has been found. If found, check if az arcdata extension exists.
|
||||
const arcVersion = parseArcExtensionVersion(versionOutput.stdout);
|
||||
if (arcVersion === undefined) {
|
||||
throw AzureCLIArcExtError;
|
||||
}
|
||||
|
||||
return new AzTool(path, <string>parseVersion(versionOutput.stdout), <string>arcVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt user to install Azure CLI.
|
||||
* @param userRequested - if true this operation was requested in response to a user issued command, if false it was issued at startup by system
|
||||
* returns true if installation was done and false otherwise.
|
||||
*/
|
||||
async function promptToInstallAz(userRequested: boolean = false): Promise<boolean> {
|
||||
let response: string | undefined = loc.yes;
|
||||
const config = <AzDeployOption>getAzConfig(azCliInstallKey);
|
||||
if (userRequested) {
|
||||
Logger.show();
|
||||
Logger.log(loc.userRequestedInstall);
|
||||
}
|
||||
if (config === AzDeployOption.dontPrompt && !userRequested) {
|
||||
Logger.log(loc.skipInstall(config));
|
||||
return false;
|
||||
}
|
||||
const responses = userRequested
|
||||
? [loc.yes, loc.no]
|
||||
: [loc.yes, loc.askLater, loc.doNotAskAgain];
|
||||
if (config === AzDeployOption.prompt) {
|
||||
Logger.log(loc.promptForAzInstallLog);
|
||||
response = await vscode.window.showErrorMessage(loc.promptForAzInstall, ...responses);
|
||||
Logger.log(loc.userResponseToInstallPrompt(response));
|
||||
}
|
||||
if (response === loc.doNotAskAgain) {
|
||||
await setAzConfig(azCliInstallKey, AzDeployOption.dontPrompt);
|
||||
} else if (response === loc.yes) {
|
||||
try {
|
||||
await installAz();
|
||||
vscode.window.showInformationMessage(loc.azInstalled);
|
||||
Logger.log(loc.azInstalled);
|
||||
return true;
|
||||
} catch (err) {
|
||||
// Windows: 1602 is User cancelling installation/update - not unexpected so don't display
|
||||
if (!(err instanceof ExitCodeError) || err.code !== 1602) {
|
||||
vscode.window.showWarningMessage(loc.installError(err));
|
||||
Logger.log(loc.installError(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt user to install Azure CLI arcdata extension.
|
||||
* @param userRequested - if true this operation was requested in response to a user issued command, if false it was issued at startup by system
|
||||
* returns true if installation was done and false otherwise.
|
||||
*/
|
||||
async function promptToInstallArcdata(userRequested: boolean = false): Promise<boolean> {
|
||||
let response: string | undefined = loc.yes;
|
||||
const config = <AzDeployOption>getAzConfig(azArcdataInstallKey);
|
||||
if (userRequested) {
|
||||
Logger.show();
|
||||
Logger.log(loc.userRequestedInstall);
|
||||
}
|
||||
if (config === AzDeployOption.dontPrompt && !userRequested) {
|
||||
Logger.log(loc.skipInstall(config));
|
||||
return false;
|
||||
}
|
||||
const responses = userRequested
|
||||
? [loc.yes, loc.no]
|
||||
: [loc.yes, loc.askLater, loc.doNotAskAgain];
|
||||
if (config === AzDeployOption.prompt) {
|
||||
Logger.log(loc.promptForArcdataInstallLog);
|
||||
response = await vscode.window.showErrorMessage(loc.promptForArcdataInstall, ...responses);
|
||||
Logger.log(loc.userResponseToInstallPrompt(response));
|
||||
}
|
||||
if (response === loc.doNotAskAgain) {
|
||||
await setAzConfig(azArcdataInstallKey, AzDeployOption.dontPrompt);
|
||||
} else if (response === loc.yes) {
|
||||
try {
|
||||
await installArcdata();
|
||||
vscode.window.showInformationMessage(loc.arcdataInstalled);
|
||||
Logger.log(loc.arcdataInstalled);
|
||||
return true;
|
||||
} catch (err) {
|
||||
// Windows: 1602 is User cancelling installation/update - not unexpected so don't display
|
||||
if (!(err instanceof ExitCodeError) || err.code !== 1602) {
|
||||
vscode.window.showWarningMessage(loc.installError(err));
|
||||
Logger.log(loc.installError(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* runs the commands to install az, downloading the installation package if needed
|
||||
*/
|
||||
export async function installAz(): Promise<void> {
|
||||
Logger.show();
|
||||
Logger.log(loc.installingAz);
|
||||
await vscode.window.withProgress(
|
||||
{
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: loc.installingAz,
|
||||
cancellable: false
|
||||
},
|
||||
async (_progress, _token): Promise<void> => {
|
||||
switch (process.platform) {
|
||||
case 'win32':
|
||||
await downloadAndInstallAzWin32();
|
||||
break;
|
||||
case 'darwin':
|
||||
await installAzDarwin();
|
||||
break;
|
||||
case 'linux':
|
||||
await installAzLinux();
|
||||
break;
|
||||
default:
|
||||
throw new Error(loc.platformUnsupported(process.platform));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the Windows installer and runs it
|
||||
*/
|
||||
async function downloadAndInstallAzWin32(): Promise<void> {
|
||||
const downLoadLink = `${azHostname}/${azUri}`;
|
||||
const downloadFolder = os.tmpdir();
|
||||
const downloadLogs = path.join(downloadFolder, 'ads_az_install_logs.log');
|
||||
const downloadedFile = await HttpClient.downloadFile(downLoadLink, downloadFolder);
|
||||
|
||||
try {
|
||||
await executeSudoCommand(`msiexec /qn /i "${downloadedFile}" /lvx "${downloadLogs}"`);
|
||||
} catch (err) {
|
||||
throw new Error(`${err.message}. See logs at ${downloadLogs} for more details.`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs commands to install az on MacOS
|
||||
*/
|
||||
async function installAzDarwin(): Promise<void> {
|
||||
await executeCommand('brew', ['update']);
|
||||
await executeCommand('brew', ['install', 'azure-cli']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs commands to install az on Linux
|
||||
*/
|
||||
async function installAzLinux(): Promise<void> {
|
||||
// Get packages needed for install process
|
||||
await executeSudoCommand('apt-get update');
|
||||
await executeSudoCommand('apt-get install ca-certificates curl apt-transport-https lsb-release gnupg');
|
||||
// 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.gpg > /dev/null');
|
||||
// Add the az repository information
|
||||
await executeSudoCommand('AZ_REPO=$(lsb_release -cs) echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $AZ_REPO main" | sudo tee /etc/apt/sources.list.d/azure-cli.list');
|
||||
// Update repository information and install az
|
||||
await executeSudoCommand('apt-get update');
|
||||
await executeSudoCommand('apt-get install azure-cli');
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the command to install az arcdata extension
|
||||
*/
|
||||
export async function installArcdata(): Promise<void> {
|
||||
Logger.show();
|
||||
Logger.log(loc.installingArcdata);
|
||||
await vscode.window.withProgress(
|
||||
{
|
||||
location: vscode.ProgressLocation.Notification,
|
||||
title: loc.installingArcdata,
|
||||
cancellable: false
|
||||
},
|
||||
async (_progress, _token): Promise<void> => {
|
||||
await executeCommand('az', ['extension', 'add', '--name', 'arcdata']);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses out the Azure CLI version from the raw az version output
|
||||
* @param raw The raw version output from az --version
|
||||
*/
|
||||
function parseVersion(raw: string): string {
|
||||
function parseVersion(raw: string): string | undefined {
|
||||
// Currently the version is a multi-line string that contains other version information such
|
||||
// as the Python installation, with the first line holding the version of az itself.
|
||||
//
|
||||
// The output of az --version looks like:
|
||||
// azure-cli 2.26.1
|
||||
// ...
|
||||
const start = raw.search('azure-cli');
|
||||
const end = raw.search('core');
|
||||
raw = raw.slice(start, end).replace('azure-cli', '');
|
||||
return raw.trim();
|
||||
const exp = /azure-cli\s*(\d*.\d*.\d*)/;
|
||||
return exp.exec(raw)?.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses out the arcdata extension version from the raw az version output
|
||||
* @param raw The raw version output from az --version
|
||||
*/
|
||||
function parseArcExtensionVersion(raw: string): string {
|
||||
function parseArcExtensionVersion(raw: string): string | undefined {
|
||||
// Currently the version is a multi-line string that contains other version information such
|
||||
// as the Python installation and any extensions.
|
||||
//
|
||||
@@ -266,15 +506,8 @@ function parseArcExtensionVersion(raw: string): string {
|
||||
// arcdata 1.0.0
|
||||
// connectedk8s 1.1.5
|
||||
// ...
|
||||
const start = raw.search('arcdata');
|
||||
if (start === -1) {
|
||||
// Commented the install/update prompts out until DoNotAskAgain is implemented
|
||||
//throw new AzureCLIArcExtError();
|
||||
} else {
|
||||
raw = raw.slice(start + 7);
|
||||
raw = raw.split(os.EOL)[0].trim();
|
||||
}
|
||||
return raw.trim();
|
||||
const exp = /arcdata\s*(\d*.\d*.\d*)/;
|
||||
return exp.exec(raw)?.pop();
|
||||
}
|
||||
|
||||
async function executeAzCommand(command: string, args: string[], additionalEnvVars: azExt.AdditionalEnvVars = {}): Promise<ProcessOutput> {
|
||||
@@ -285,36 +518,13 @@ async function executeAzCommand(command: string, args: string[], additionalEnvVa
|
||||
return executeCommand(command, args, additionalEnvVars);
|
||||
}
|
||||
|
||||
// Commented the install/update prompts out until DoNotAskAgain is implemented
|
||||
// async function setConfig(key: string, value: string): Promise<void> {
|
||||
// const config = vscode.workspace.getConfiguration(azConfigSection);
|
||||
// await config.update(key, value, vscode.ConfigurationTarget.Global);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Find user's local Azure CLI. Execute az --version and parse out the version number.
|
||||
* If an update is needed, prompt the user to update via link. Return the AzTool.
|
||||
* Currently commented out because Don't Prompt Again is not properly implemented.
|
||||
*/
|
||||
async function findSpecificAz(): Promise<IAzTool> {
|
||||
const path = await ((process.platform === 'win32') ? searchForCmd('az.cmd') : searchForCmd('az'));
|
||||
const versionOutput = await executeAzCommand(`"${path}"`, ['--version']);
|
||||
const version = parseArcExtensionVersion(versionOutput.stdout);
|
||||
const semVersion = new SemVer(version);
|
||||
//let response: string | undefined;
|
||||
|
||||
if (LATEST_AZ_ARC_EXTENSION_VERSION.compare(semVersion) === 1) {
|
||||
// If there is a greater version of az arc extension available, prompt to update
|
||||
// Commented the install/update prompts out until DoNotAskAgain is implemented
|
||||
// const responses = [loc.askLater, loc.doNotAskAgain];
|
||||
// response = await vscode.window.showInformationMessage(loc.requiredArcDataVersionNotAvailable(latestAzArcExtensionVersion, version), ...responses);
|
||||
// if (response === loc.doNotAskAgain) {
|
||||
// await setConfig(azRequiredUpdateKey, AzDeployOption.dontPrompt);
|
||||
// }
|
||||
} else if (LATEST_AZ_ARC_EXTENSION_VERSION.compare(semVersion) === -1) {
|
||||
// Current version should not be greater than latest version
|
||||
// Commented the install/update prompts out until DoNotAskAgain is implemented
|
||||
// vscode.window.showErrorMessage(loc.unsupportedArcDataVersion(latestAzArcExtensionVersion, version));
|
||||
}
|
||||
return new AzTool(path, version);
|
||||
function getAzConfig(key: string): AzDeployOption | undefined {
|
||||
const config = vscode.workspace.getConfiguration(azConfigSection);
|
||||
const value = <AzDeployOption>config.get<AzDeployOption>(key);
|
||||
return value;
|
||||
}
|
||||
|
||||
async function setAzConfig(key: string, value: string): Promise<void> {
|
||||
const config = vscode.workspace.getConfiguration(azConfigSection);
|
||||
await config.update(key, value, vscode.ConfigurationTarget.Global);
|
||||
}
|
||||
|
||||
15
extensions/azcli/src/azReleaseInfo.ts
Normal file
15
extensions/azcli/src/azReleaseInfo.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
interface PlatformReleaseInfo {
|
||||
version: string; // "20.0.1"
|
||||
link?: string; // "https://aka.ms/az-msi"
|
||||
}
|
||||
|
||||
export interface AzReleaseInfo {
|
||||
win32: PlatformReleaseInfo,
|
||||
darwin: PlatformReleaseInfo,
|
||||
linux: PlatformReleaseInfo
|
||||
}
|
||||
@@ -6,11 +6,15 @@
|
||||
// config setting keys
|
||||
export const azConfigSection: string = 'azcli';
|
||||
export const debugConfigKey = 'logDebugInfo';
|
||||
export const azRequiredUpdateKey: string = 'requiredUpdate';
|
||||
|
||||
export const azArcdataInstallKey: string = 'arcdataInstall';
|
||||
export const azArcdataUpdateKey: string = 'arcdataUpdate';
|
||||
export const azCliInstallKey: string = 'azInstall';
|
||||
|
||||
// context keys && memento keys
|
||||
export const azFound = 'az.found';
|
||||
export const azArcFound = 'az.arcfound';
|
||||
|
||||
// other constants
|
||||
export const azHostname = 'https://aka.ms';
|
||||
export const azUri = 'installazurecliwindows';
|
||||
export const latestAzArcExtensionVersion = '1.0.0';
|
||||
|
||||
@@ -7,16 +7,26 @@ import * as azExt from 'az-ext';
|
||||
import * as rd from 'resource-deployment';
|
||||
import * as vscode from 'vscode';
|
||||
import { getExtensionApi } from './api';
|
||||
import { findAz } from './az';
|
||||
import { checkAndInstallAz } from './az';
|
||||
import { ArcControllerConfigProfilesOptionsSource } from './providers/arcControllerConfigProfilesOptionsSource';
|
||||
import { AzToolService } from './services/azToolService';
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<azExt.IExtension> {
|
||||
const azToolService = new AzToolService();
|
||||
vscode.commands.registerCommand('az.install', async () => {
|
||||
azToolService.localAz = await checkAndInstallAz(true /* userRequested */);
|
||||
});
|
||||
|
||||
azToolService.localAz = await findAz();
|
||||
// Don't block on this since we want the extension to finish activating without needing user input
|
||||
const localAzDiscovered = checkAndInstallAz() // install if not installed and user wants it.
|
||||
.then(async azTool => {
|
||||
if (azTool !== undefined) {
|
||||
azToolService.localAz = azTool;
|
||||
}
|
||||
return azTool;
|
||||
});
|
||||
|
||||
const azApi = getExtensionApi(azToolService);
|
||||
const azApi = getExtensionApi(azToolService, localAzDiscovered);
|
||||
|
||||
// register option source(s)
|
||||
const rdApi = <rd.IExtension>vscode.extensions.getExtension(rd.extension.name)?.exports;
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import { getErrorMessage } from './common/utils';
|
||||
import { azCliInstallKey, azConfigSection } from './constants';
|
||||
// import { azCliInstallKey } from './constants';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export const az = localize('az.az', "Azure CLI");
|
||||
export const searchingForAz = localize('az.searchingForAz', "Searching for existing Azure CLI installation...");
|
||||
export const foundExistingAz = (path: string, version: string): string => localize('az.foundExistingAz', "Found existing Azure CLI installation of version (v{0}) at path:{1}", version, path);
|
||||
export const foundExistingAz = (path: string, versionAz: string, versionArc: string): string => localize('az.foundExistingAz', "Found existing Azure CLI installation of version (v{0}) at path:{1} with arcdata version: {2}.", versionAz, path, versionArc);
|
||||
export const downloadingProgressMb = (currentMb: string, totalMb: string): string => localize('az.downloadingProgressMb', "Downloading ({0} / {1} MB)", currentMb, totalMb);
|
||||
export const downloadFinished = localize('az.downloadFinished', "Download finished");
|
||||
export const downloadingTo = (name: string, url: string, location: string): string => localize('az.downloadingTo', "Downloading {0} from {1} to {2}", name, url, location);
|
||||
@@ -26,10 +28,47 @@ export const noReleaseVersion = (platform: string, releaseInfo: string): string
|
||||
export const noDownloadLink = (platform: string, releaseInfo: string): string => localize('az.noDownloadLink', "No download link available for platform '{0}'\nRelease info: ${1}", platform, releaseInfo);
|
||||
export const failedToParseReleaseInfo = (url: string, fileContents: string, err: any): string => localize('az.failedToParseReleaseInfo', "Failed to parse the JSON of contents at: {0}.\nFile contents:\n{1}\nError: {2}", url, fileContents, getErrorMessage(err));
|
||||
export const endpointOrNamespaceRequired = localize('az.endpointOrNamespaceRequired', "Either an endpoint or a namespace must be specified");
|
||||
export const arcdataExtensionNotInstalled = localize('az.arcdataExtensionNotInstalled', "This extension requires the Azure CLI extension 'arcdata' to be installed. Install the latest version manually from [here](https://docs.microsoft.com/azure/azure-arc/data/install-arcdata-extension) and then restart Azure Data Studio.");
|
||||
export const noAzureCLI = localize('az.noAzureCLI', "No Azure CLI is available. Install the latest version manually from [here](https://docs.microsoft.com/cli/azure/install-azure-cli) and then restart Azure Data Studio.");
|
||||
export const requiredArcDataVersionNotAvailable = (requiredVersion: string, currentVersion: string): string => localize('az.requiredVersionNotAvailable', "This extension requires the Azure CLI extension 'arcdata' version >= {0} to be installed, but the current version available is only {1}. Install the correct version manually from [here](https://docs.microsoft.com/azure/azure-arc/data/install-arcdata-extension) and then restart Azure Data Studio.", requiredVersion, currentVersion);
|
||||
export const unsupportedArcDataVersion = (requiredVersion: string, currentVersion: string): string => localize('az.unsupportedArcDataVersion', "Your downloaded version {1} of the Azure CLI extension 'arcdata' is not yet supported. The latest version is is {0}. Install the correct version manually from [here](https://docs.microsoft.com/azure/azure-arc/data/install-arcdata-extension) and then restart Azure Data Studio.", requiredVersion, currentVersion);
|
||||
export const arcdataExtensionNotInstalled = localize('az.arcdataExtensionNotInstalled', "This extension requires the Azure CLI extension 'arcdata' to be installed. Install the latest version using instructions from [here](https://docs.microsoft.com/azure/azure-arc/data/install-arcdata-extension).");
|
||||
export const noAzureCLI = localize('az.noAzureCLI', "No Azure CLI is available. Install the latest version manually from [here](https://docs.microsoft.com/cli/azure/install-azure-cli) and then restart Azure Studio.");
|
||||
export const requiredArcDataVersionNotAvailable = (requiredVersion: string, currentVersion: string): string => localize('az.requiredVersionNotAvailable', "This extension requires the Azure CLI extension 'arcdata' version >= {0} to be installed, but the current version available is only {1}. Install the correct version using instructions from [here](https://docs.microsoft.com/azure/azure-arc/data/install-arcdata-extension).", requiredVersion, currentVersion);
|
||||
export const unsupportedArcDataVersion = (requiredVersion: string, currentVersion: string): string => localize('az.unsupportedArcDataVersion', "Your downloaded version {1} of the Azure CLI extension 'arcdata' is not yet supported. The latest version is is {0}. Install the correct version using instructions from [here](https://docs.microsoft.com/azure/azure-arc/data/install-arcdata-extension).", requiredVersion, currentVersion);
|
||||
export const doNotAskAgain = localize('az.doNotAskAgain', "Don't Ask Again");
|
||||
export const askLater = localize('az.askLater', "Ask Later");
|
||||
export const azOutputParseErrorCaught = (command: string): string => localize('az.azOutputParseErrorCaught', "An error occurred while parsing the output of az command: {0}. The output is not JSON.", command);
|
||||
export const parseVersionError = localize('az.parseVersionError', "An error occurred while parsing the output of az --version.");
|
||||
export const installingAz = localize('az.installingAz', "Installing Azure CLI...");
|
||||
export const installingArcdata = localize('az.installingArcdata', "Installing the Azure CLI arcdata extension...");
|
||||
export const updatingAz = localize('az.updatingAz', "Updating Azure CLI...");
|
||||
export const azInstalled = localize('az.azInstalled', "Azure CLI was successfully installed. Restarting Azure Studio is required to complete configuration - features will not be activated until this is done.");
|
||||
export const arcdataInstalled = localize('az.arcdataInstalled', "The Azure CLI arcdata extension was successfully installed. Restarting Azure Studio is required to complete configuration - features will not be activated until this is done.");
|
||||
export const yes = localize('az.yes', "Yes");
|
||||
export const no = localize('az.no', "No");
|
||||
export const accept = localize('az.accept', "Accept");
|
||||
export const decline = localize('az.decline', "Decline");
|
||||
export const checkingLatestAzVersion = localize('az.checkingLatestAzVersion', "Checking for latest available version of Azure CLI");
|
||||
export const foundAzVersionToUpdateTo = (newVersion: string, currentVersion: string): string => localize('az.versionForUpdate', "Found version: {0} that Azure CLI can be updated to from current version: {1}.", newVersion, currentVersion);
|
||||
export const latestAzVersionAvailable = (version: string): string => localize('az.latestAzVersionAvailable', "Latest available Azure CLI version: {0}.", version);
|
||||
export const couldNotFindAz = (err: any): string => localize('az.couldNotFindAz', "Could not find Azure CLI. Error: {0}", err.message ?? err);
|
||||
export const couldNotFindAzArc = (err: any): string => localize('az.couldNotFindAzArc', "Could not find the Azure CLI arcdata extension. Error: {0}", err.message ?? err);
|
||||
export const currentlyInstalledVersionIsLatest = (currentVersion: string): string => localize('az.currentlyInstalledVersionIsLatest', "Currently installed version of Azure CLI: {0} is same or newer than any other version available", currentVersion);
|
||||
export const promptForAzInstall = localize('az.couldNotFindAzWithPrompt', "Could not find Azure CLI, install it now? If not then some features will not be able to function.");
|
||||
export const promptForAzInstallLog = promptLog(promptForAzInstall);
|
||||
export const promptForArcdataInstall = localize('az.couldNotFindArcdataWithPrompt', "Could not find the Azure CLI arcdata extension, install it now? If not then some features will not be able to function.");
|
||||
export const promptForArcdataInstallLog = promptLog(promptForArcdataInstall);
|
||||
export const promptForAzUpdate = (version: string): string => localize('az.promptForAzUpdate', "A new version of Azure CLI ( {0} ) is available, do you wish to update to it now?", version);
|
||||
export const promptForRequiredAzUpdate = (requiredVersion: string, latestVersion: string): string => localize('az.promptForRequiredAzUpdate', "This extension requires Azure CLI >= {0} to be installed, do you wish to update to the latest version ({1}) now? If you do not then some functionality may not work.", requiredVersion, latestVersion);
|
||||
export const promptForAzUpdateLog = (version: string): string => promptLog(promptForAzUpdate(version));
|
||||
export const promptForRequiredAzUpdateLog = (requiredVersion: string, latestVersion: string): string => promptLog(promptForRequiredAzUpdate(requiredVersion, latestVersion));
|
||||
export const missingRequiredVersion = (requiredVersion: string): string => localize('az.missingRequiredVersion', "Azure CLI >= {0} is required for this feature. Run the 'Azure CLI: Check for Update' command to install this and then try again.", requiredVersion);
|
||||
export const installError = (err: any): string => localize('az.installError', "Error installing Azure CLI and arcdata extension: {0}", err.message ?? err);
|
||||
export const updateError = (err: any): string => localize('az.updateError', "Error updating Azure CLI: {0}", err.message ?? err);
|
||||
export const noAz = localize('az.noAz', "No Azure CLI is available, run the command 'Azure CLI: Install' to enable the features that require it.");
|
||||
export const noAzArc = localize('az.noAzArc', "No Azure CLI arcdata extension is available.");
|
||||
export const noAzWithLink = localize('az.noAzWithLink', "No Azure CLI is available, [install the Azure CLI](command:az.install) to enable the features that require it.");
|
||||
export const skipInstall = (config: string): string => localize('az.skipInstall', "Skipping installation of Azure CLI and arcdata extension, since the operation was not user requested and config option: {0}.{1} is {2}", azConfigSection, azCliInstallKey, config);
|
||||
export const azUserSettingRead = (configName: string, configValue: string): string => localize('az.azUserSettingReadLog', "Azure CLI user setting: {0}.{1} read, value: {2}", azConfigSection, configName, configValue);
|
||||
export const azUserSettingUpdated = (configName: string, configValue: string): string => localize('az.azUserSettingUpdatedLog', "Azure CLI user setting: {0}.{1} updated, newValue: {2}", azConfigSection, configName, configValue);
|
||||
export const userResponseToInstallPrompt = (response: string | undefined): string => localize('az.userResponseInstall', "User Response on prompt to install Azure CLI: {0}", response);
|
||||
export const userResponseToUpdatePrompt = (response: string | undefined): string => localize('az.userResponseUpdate', "User Response on prompt to update Azure CLI: {0}", response);
|
||||
export const userRequestedInstall = localize('az.userRequestedInstall', "User requested to install Azure CLI and arcdata extension using 'Azure CLI: Install' command");
|
||||
export const updateCheckSkipped = localize('az.updateCheckSkipped', "No check for new Azure CLI version availability performed as Azure CLI was not found to be installed");
|
||||
|
||||
151
extensions/azcli/src/test/az.test.ts
Normal file
151
extensions/azcli/src/test/az.test.ts
Normal file
@@ -0,0 +1,151 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as should from 'should';
|
||||
import * as sinon from 'sinon';
|
||||
import * as childProcess from '../common/childProcess';
|
||||
import * as az from '../az';
|
||||
|
||||
describe('az', function () {
|
||||
afterEach(function (): void {
|
||||
sinon.restore();
|
||||
});
|
||||
describe('azTool', function (): void {
|
||||
const azTool = new az.AzTool('my path', '2.26.0', '1.0.0');
|
||||
let executeCommandStub: sinon.SinonStub;
|
||||
const namespace = 'arc';
|
||||
const name = 'arcdc';
|
||||
|
||||
beforeEach(function (): void {
|
||||
executeCommandStub = sinon.stub(childProcess, 'executeCommand').resolves({ stdout: '{}', stderr: '' });
|
||||
});
|
||||
|
||||
describe('arcdata', function (): void {
|
||||
describe('dc', function (): void {
|
||||
describe('endpoint', async function (): Promise<void> {
|
||||
it('list', async function (): Promise<void> {
|
||||
await azTool.arcdata.dc.endpoint.list(namespace);
|
||||
verifyExecuteCommandCalledWithArgs(['arcdata', 'dc', 'endpoint', 'list', '--k8s-namespace', namespace, '--use-k8s']);
|
||||
});
|
||||
});
|
||||
describe('config', async function (): Promise<void> {
|
||||
it('list', async function (): Promise<void> {
|
||||
await azTool.arcdata.dc.config.list();
|
||||
verifyExecuteCommandCalledWithArgs(['arcdata', 'dc', 'config', 'list']);
|
||||
});
|
||||
it('show', async function (): Promise<void> {
|
||||
await azTool.arcdata.dc.config.show(namespace);
|
||||
verifyExecuteCommandCalledWithArgs(['arcdata', 'dc', 'config', 'show', '--k8s-namespace', namespace, '--use-k8s']);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('postgres', function (): void {
|
||||
describe('arc-server', function (): void {
|
||||
it('delete', async function (): Promise<void> {
|
||||
await azTool.postgres.arcserver.delete(name, namespace);
|
||||
verifyExecuteCommandCalledWithArgs(['postgres', 'arc-server', 'delete', name, '--k8s-namespace', namespace]);
|
||||
});
|
||||
it('list', async function (): Promise<void> {
|
||||
await azTool.postgres.arcserver.list(namespace);
|
||||
verifyExecuteCommandCalledWithArgs(['postgres', 'arc-server', 'list', '--k8s-namespace', namespace]);
|
||||
});
|
||||
it('show', async function (): Promise<void> {
|
||||
await azTool.postgres.arcserver.show(name, namespace);
|
||||
verifyExecuteCommandCalledWithArgs(['postgres', 'arc-server', 'show', name, '--k8s-namespace', namespace]);
|
||||
});
|
||||
it('edit', async function (): Promise<void> {
|
||||
const args = {
|
||||
adminPassword: true,
|
||||
coresLimit: 'myCoresLimit',
|
||||
coresRequest: 'myCoresRequest',
|
||||
engineSettings: 'myEngineSettings',
|
||||
extensions: 'myExtensions',
|
||||
memoryLimit: 'myMemoryLimit',
|
||||
memoryRequest: 'myMemoryRequest',
|
||||
noWait: true,
|
||||
port: 1337,
|
||||
replaceEngineSettings: true,
|
||||
workers: 2
|
||||
};
|
||||
await azTool.postgres.arcserver.edit(name, args, namespace);
|
||||
verifyExecuteCommandCalledWithArgs([
|
||||
'postgres', 'arc-server', 'edit',
|
||||
name,
|
||||
'--admin-password',
|
||||
args.coresLimit,
|
||||
args.coresRequest,
|
||||
args.engineSettings,
|
||||
args.extensions,
|
||||
args.memoryLimit,
|
||||
args.memoryRequest,
|
||||
'--no-wait',
|
||||
args.port.toString(),
|
||||
'--replace-engine-settings',
|
||||
args.workers.toString()]);
|
||||
});
|
||||
it('edit no optional args', async function (): Promise<void> {
|
||||
await azTool.postgres.arcserver.edit(name, {}, namespace);
|
||||
verifyExecuteCommandCalledWithArgs([
|
||||
'postgres', 'arc-server', 'edit',
|
||||
name]);
|
||||
verifyExecuteCommandCalledWithoutArgs([
|
||||
'--admin-password',
|
||||
'--cores-limit',
|
||||
'--cores-request',
|
||||
'--engine-settings',
|
||||
'--extensions',
|
||||
'--memory-limit',
|
||||
'--memory-request',
|
||||
'--no-wait',
|
||||
'--port',
|
||||
'--replace-engine-settings',
|
||||
'--workers']);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('sql', function (): void {
|
||||
describe('mi-arc', function (): void {
|
||||
it('delete', async function (): Promise<void> {
|
||||
await azTool.sql.miarc.delete(name, namespace);
|
||||
verifyExecuteCommandCalledWithArgs(['sql', 'mi-arc', 'delete', name, '--k8s-namespace', namespace, '--use-k8s']);
|
||||
});
|
||||
it('list', async function (): Promise<void> {
|
||||
await azTool.sql.miarc.list(namespace);
|
||||
verifyExecuteCommandCalledWithArgs(['sql', 'mi-arc', 'list', '--k8s-namespace', namespace, '--use-k8s']);
|
||||
});
|
||||
it('show', async function (): Promise<void> {
|
||||
await azTool.sql.miarc.show(name, namespace);
|
||||
verifyExecuteCommandCalledWithArgs(['sql', 'mi-arc', 'show', name, '--k8s-namespace', namespace, '--use-k8s']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('version', async function (): Promise<void> {
|
||||
executeCommandStub.resolves({ stdout: '1.0.0', stderr: '' });
|
||||
await azTool.version();
|
||||
verifyExecuteCommandCalledWithArgs(['--version']);
|
||||
});
|
||||
|
||||
/**
|
||||
* Verifies that the specified args were included in the call to executeCommand
|
||||
* @param args The args to check were included in the execute command call
|
||||
*/
|
||||
function verifyExecuteCommandCalledWithArgs(args: string[], callIndex = 0): void {
|
||||
const commandArgs = executeCommandStub.args[callIndex][1] as string[];
|
||||
args.forEach(arg => should(commandArgs).containEql(arg));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the specified args weren't included in the call to executeCommand
|
||||
* @param args The args to check weren't included in the execute command call
|
||||
*/
|
||||
function verifyExecuteCommandCalledWithoutArgs(args: string[]): void {
|
||||
const commandArgs = executeCommandStub.args[0][1] as string[];
|
||||
args.forEach(arg => should(commandArgs).not.containEql(arg));
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -13,10 +13,10 @@ describe('az', function () {
|
||||
sinon.restore();
|
||||
});
|
||||
describe('azTool', function (): void {
|
||||
const azTool = new azdata.AzTool('C:/Program Files (x86)/Microsoft SDKs/Azure/CLI2/wbin/az.cmd', '2.26.0');
|
||||
const azTool = new azdata.AzTool('my path', '2.26.0', '1.0.0');
|
||||
let executeCommandStub: sinon.SinonStub;
|
||||
const namespace = 'arc4';
|
||||
const name = 'cy-dc-4';
|
||||
const namespace = 'arc';
|
||||
const name = 'dc';
|
||||
|
||||
beforeEach(function (): void {
|
||||
executeCommandStub = sinon.stub(childProcess, 'executeCommand').resolves({ stdout: '{}', stderr: '' });
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as path from 'path';
|
||||
const testRunner = require('vscodetestcover');
|
||||
|
||||
const suite = 'azdata Extension Tests';
|
||||
const suite = 'azcli Extension Tests';
|
||||
|
||||
const mochaOptions: any = {
|
||||
ui: 'bdd',
|
||||
|
||||
@@ -1,17 +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 should from 'should';
|
||||
import { AzTool } from '../../az';
|
||||
import { AzToolService } from '../../services/azToolService';
|
||||
|
||||
describe('azToolService', function (): void {
|
||||
it('Tool should be set correctly', async function (): Promise<void> {
|
||||
const service = new AzToolService();
|
||||
should(service.localAz).be.undefined();
|
||||
service.localAz = new AzTool('my path', '1.0.0');
|
||||
should(service).not.be.undefined();
|
||||
});
|
||||
});
|
||||
10
extensions/azcli/src/typings/az-ext.d.ts
vendored
10
extensions/azcli/src/typings/az-ext.d.ts
vendored
@@ -327,11 +327,17 @@ declare module 'az-ext' {
|
||||
},
|
||||
getPath(): Promise<string>,
|
||||
/**
|
||||
* The semVersion corresponding to this installation of az. version() method should have been run
|
||||
* The semVersion corresponding to this installation of the Azure CLI. version() method should have been run
|
||||
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
|
||||
* Az has gotten reinstalled in the background after this IAzApi object was constructed.
|
||||
*/
|
||||
getSemVersion(): Promise<SemVer>,
|
||||
getSemVersionAz(): Promise<SemVer>,
|
||||
/**
|
||||
* The semVersion corresponding to this installation of the Azure CLI arcdata extension. version() method should
|
||||
* have been run before fetching this value to ensure that correct value is returned. This is almost always
|
||||
* correct unless az arcdata has gotten reinstalled in the background after this IAzApi object was constructed.
|
||||
*/
|
||||
getSemVersionArc(): Promise<SemVer>,
|
||||
version(): Promise<AzOutput<string>>
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user