Remove azdata eula acceptance from arc deployments (#12292)

* saving to switch tasks

* activate to exports in extApi

* working version - cleanup pending

* improve messages

* apply pr feedback from a different review

* remove unneeded strings

* redo apiService

* remove async from getVersionFromOutput

* remove _ prefix from protected fields

* error message fix

* throw specif errors from azdata extension

* arrow methods to regular methods

* pr feedback

* expand azdata extension api

* pr feedback

* remove unused var

* pr feedback
This commit is contained in:
Arvind Ranasaria
2020-09-17 11:20:32 -07:00
committed by GitHub
parent 945e04ed92
commit ba44a2f02e
17 changed files with 181 additions and 114 deletions

View File

@@ -553,20 +553,7 @@
],
"when": true
}
],
"agreement": {
"template": "%arc.control.plane.arc.data.controller.agreement%",
"links": [
{
"text": "%microsoft.agreement.privacy.statement%",
"url": "https://go.microsoft.com/fwlink/?LinkId=853010"
},
{
"text": "%arc.agreement.azdata.eula%",
"url": "https://aka.ms/eula-azdata-en"
}
]
}
]
},
{
"name": "arc.sql",
@@ -577,18 +564,6 @@
"light": "./images/miaa.svg",
"dark": "./images/miaa.svg"
},
"options": [
{
"name": "resourceType",
"displayName": "%resource.type.picker.display.name%",
"values": [
{
"name": "sql.managed.instance",
"displayName": "%sql.managed.instance.display.name%"
}
]
}
],
"providers": [
{
"dialog": {
@@ -706,7 +681,7 @@
"version": "20.2.0"
}
],
"when": "resourceType=sql.managed.instance"
"when": "true"
}
],
"agreement": {
@@ -719,10 +694,6 @@
{
"text": "%arc.agreement.sql.terms.conditions%",
"url": "https://go.microsoft.com/fwlink/?linkid=2045708"
},
{
"text": "%arc.agreement.azdata.eula%",
"url": "https://aka.ms/eula-azdata-en"
}
]
}
@@ -736,18 +707,6 @@
"light": "./images/postgres.svg",
"dark": "./images/postgres.svg"
},
"options": [
{
"name": "resourceType",
"displayName": "%resource.type.picker.display.name%",
"values": [
{
"name": "postgres",
"displayName": "%postgres.server.group.display.name%"
}
]
}
],
"providers": [
{
"dialog": {
@@ -926,7 +885,7 @@
"version": "20.2.0"
}
],
"when": "resourceType=postgres"
"when": "true"
}
],
"agreement": {
@@ -939,10 +898,6 @@
{
"text": "%arc.agreement.postgres.terms.conditions%",
"url": "https://go.microsoft.com/fwlink/?linkid=2045708"
},
{
"text": "%arc.agreement.azdata.eula%",
"url": "https://aka.ms/eula-azdata-en"
}
]
}

View File

@@ -65,7 +65,6 @@
"arc.control.plane.summary.location": "Location",
"arc.control.plane.arc.data.controller.agreement": "I accept {0} and {1}.",
"microsoft.agreement.privacy.statement":"Microsoft Privacy Statement",
"arc.agreement.azdata.eula":"azdata license terms",
"deploy.arc.control.plane.action":"Script to notebook",
@@ -73,12 +72,9 @@
"resource.type.arc.postgres.display.name": "PostgreSQL Hyperscale server groups - Azure Arc (preview)",
"resource.type.arc.sql.description": "Managed SQL Instance service for app developers in a customer-managed environment",
"resource.type.arc.postgres.description": "Deploy PostgreSQL server groups into an Azure Arc environment",
"resource.type.picker.display.name": "Resource Type",
"sql.managed.instance.display.name": "Azure SQL managed instance - Azure Arc",
"postgres.server.group.display.name": "PostgreSQL server groups - Azure Arc",
"arc.controller": "Target Azure Arc Controller",
"arc.sql.new.dialog.title": "Deploy Azure SQL managed instance - Azure Arc (preview)",
"arc.sql.settings.section.title": "SQL Connection information",
"arc.azure.section.title": "Azure information",
@@ -130,7 +126,7 @@
"arc.postgres.server.group.cores.description": "Fractional cores are supported",
"arc.postgres.server.group.memory.request": "Min memory MB (per node) to reserve",
"arc.postgres.server.group.memory.limit": "Max memory MB (per node) to allow",
"arc.agreement": "I accept {0}, {1} and {2}.",
"arc.agreement": "I accept {0} and {1}.",
"arc.agreement.sql.terms.conditions":"Azure SQL managed instance - Azure Arc terms and conditions",
"arc.agreement.postgres.terms.conditions":"PostgreSQL server groups - Azure Arc terms and conditions",
"arc.deploy.action":"Deploy"

View File

@@ -24,9 +24,6 @@ const enum AzdataDeployOption {
* Interface for an object to interact with the azdata tool installed on the box.
*/
export interface IAzdataTool extends azdataExt.IAzdataApi {
path: string,
cachedVersion: SemVer
/**
* Executes azdata with the specified arguments (e.g. --version) and returns the result
* @param args The args to pass to azdata
@@ -39,9 +36,26 @@ export interface IAzdataTool extends azdataExt.IAzdataApi {
* An object to interact with the azdata tool installed on the box.
*/
export class AzdataTool implements IAzdataTool {
public cachedVersion: SemVer;
constructor(public path: string, version: string) {
this.cachedVersion = new SemVer(version);
private _semVersion: SemVer;
constructor(private _path: string, version: string) {
this._semVersion = new SemVer(version);
}
/**
* The semVersion corresponding to this installation of azdata. version() method should have been run
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
* Azdata has gotten reinstalled in the background after this IAzdataApi object was constructed.
*/
public getSemVersion() {
return this._semVersion;
}
/**
* gets the path where azdata tool is installed
*/
public getPath() {
return this._path;
}
public arc = {
@@ -141,8 +155,8 @@ export class AzdataTool implements IAzdataTool {
* It also updates the cachedVersion property based on the return value from the tool.
*/
public async version(): Promise<azdataExt.AzdataOutput<string>> {
const output = await executeAzdataCommand(`"${this.path}"`, ['--version']);
this.cachedVersion = new SemVer(parseVersion(output.stdout));
const output = await executeAzdataCommand(`"${this._path}"`, ['--version']);
this._semVersion = new SemVer(parseVersion(output.stdout));
return {
logs: [],
stdout: output.stdout.split(os.EOL),
@@ -153,7 +167,7 @@ export class AzdataTool implements IAzdataTool {
public async executeCommand<R>(args: string[], additionalEnvVars?: { [key: string]: string }): Promise<azdataExt.AzdataOutput<R>> {
try {
const output = JSON.parse((await executeAzdataCommand(`"${this.path}"`, args.concat(['--output', 'json']), additionalEnvVars)).stdout);
const output = JSON.parse((await executeAzdataCommand(`"${this._path}"`, args.concat(['--output', 'json']), additionalEnvVars)).stdout);
return {
logs: <string[]>output.log,
stdout: <string[]>output.stdout,
@@ -172,7 +186,7 @@ export class AzdataTool implements IAzdataTool {
// it means this was probably some other generic error (such as command not being found)
// check if azdata still exists if it does then rethrow the original error if not then emit a new specific error.
try {
await fs.promises.access(this.path);
await fs.promises.access(this._path);
//this.path exists
throw err; // rethrow the error
} catch (e) {
@@ -207,7 +221,7 @@ export async function findAzdata(): Promise<IAzdataTool> {
try {
const azdata = await findSpecificAzdata();
await vscode.commands.executeCommand('setContext', azdataFound, true); // save a context key that azdata was found so that command for installing azdata is no longer available in commandPalette and that for updating it is.
Logger.log(loc.foundExistingAzdata(azdata.path, azdata.cachedVersion.raw));
Logger.log(loc.foundExistingAzdata(azdata.getPath(), azdata.getSemVersion().raw));
return azdata;
} catch (err) {
Logger.log(loc.couldNotFindAzdata(err));
@@ -293,12 +307,12 @@ export async function checkAndInstallAzdata(userRequested: boolean = false): Pro
*/
export async function checkAndUpdateAzdata(currentAzdata?: IAzdataTool, userRequested: boolean = false): Promise<boolean> {
if (currentAzdata !== undefined) {
const newVersion = await discoverLatestAvailableAzdataVersion();
if (newVersion.compare(currentAzdata.cachedVersion) === 1) {
Logger.log(loc.foundAzdataVersionToUpdateTo(newVersion.raw, currentAzdata.cachedVersion.raw));
return await promptToUpdateAzdata(newVersion.raw, userRequested);
const newSemVersion = await discoverLatestAvailableAzdataVersion();
if (newSemVersion.compare(currentAzdata.getSemVersion()) === 1) {
Logger.log(loc.foundAzdataVersionToUpdateTo(newSemVersion.raw, currentAzdata.getSemVersion().raw));
return await promptToUpdateAzdata(newSemVersion.raw, userRequested);
} else {
Logger.log(loc.currentlyInstalledVersionIsLatest(currentAzdata.cachedVersion.raw));
Logger.log(loc.currentlyInstalledVersionIsLatest(currentAzdata.getSemVersion().raw));
}
} else {
Logger.log(loc.updateCheckSkipped);

View File

@@ -12,7 +12,6 @@ import * as loc from './localizedConstants';
let localAzdata: IAzdataTool | undefined = undefined;
let eulaAccepted: boolean = false;
export async function activate(context: vscode.ExtensionContext): Promise<azdataExt.IExtension> {
vscode.commands.registerCommand('azdata.acceptEula', async () => {
eulaAccepted = await promptForEula(context.globalState, true /* userRequested */);
@@ -59,26 +58,27 @@ export async function activate(context: vscode.ExtensionContext): Promise<azdata
});
return {
isEulaAccepted: () => !!context.globalState.get<boolean>(constants.eulaAccepted),
azdata: {
arc: {
dc: {
create: async (namespace: string, name: string, connectivityMode: string, resourceGroup: string, location: string, subscription: string, profileName?: string, storageClass?: string) => {
await throwIfNoAzdataOrEulaNotAccepted();
throwIfNoAzdataOrEulaNotAccepted();
return localAzdata!.arc.dc.create(namespace, name, connectivityMode, resourceGroup, location, subscription, profileName, storageClass);
},
endpoint: {
list: async () => {
await throwIfNoAzdataOrEulaNotAccepted();
throwIfNoAzdataOrEulaNotAccepted();
return localAzdata!.arc.dc.endpoint.list();
}
},
config: {
list: async () => {
await throwIfNoAzdataOrEulaNotAccepted();
throwIfNoAzdataOrEulaNotAccepted();
return localAzdata!.arc.dc.config.list();
},
show: async () => {
await throwIfNoAzdataOrEulaNotAccepted();
throwIfNoAzdataOrEulaNotAccepted();
return localAzdata!.arc.dc.config.show();
}
}
@@ -90,11 +90,11 @@ export async function activate(context: vscode.ExtensionContext): Promise<azdata
return localAzdata!.arc.postgres.server.delete(name);
},
list: async () => {
await throwIfNoAzdataOrEulaNotAccepted();
throwIfNoAzdataOrEulaNotAccepted();
return localAzdata!.arc.postgres.server.list();
},
show: async (name: string) => {
await throwIfNoAzdataOrEulaNotAccepted();
throwIfNoAzdataOrEulaNotAccepted();
return localAzdata!.arc.postgres.server.show(name);
},
edit: async (args: {
@@ -119,41 +119,53 @@ export async function activate(context: vscode.ExtensionContext): Promise<azdata
sql: {
mi: {
delete: async (name: string) => {
await throwIfNoAzdataOrEulaNotAccepted();
throwIfNoAzdataOrEulaNotAccepted();
return localAzdata!.arc.sql.mi.delete(name);
},
list: async () => {
await throwIfNoAzdataOrEulaNotAccepted();
throwIfNoAzdataOrEulaNotAccepted();
return localAzdata!.arc.sql.mi.list();
},
show: async (name: string) => {
await throwIfNoAzdataOrEulaNotAccepted();
throwIfNoAzdataOrEulaNotAccepted();
return localAzdata!.arc.sql.mi.show(name);
}
}
}
},
getPath: () => {
throwIfNoAzdata();
return localAzdata!.getPath();
},
login: async (endpoint: string, username: string, password: string) => {
await throwIfNoAzdataOrEulaNotAccepted();
throwIfNoAzdataOrEulaNotAccepted();
return localAzdata!.login(endpoint, username, password);
},
getSemVersion: () => {
throwIfNoAzdata();
return localAzdata!.getSemVersion();
},
version: async () => {
await throwIfNoAzdataOrEulaNotAccepted();
throwIfNoAzdata();
return localAzdata!.version();
}
}
};
}
async function throwIfNoAzdataOrEulaNotAccepted(): Promise<void> {
if (!localAzdata) {
Logger.log(loc.noAzdata);
throw new Error(loc.noAzdata);
}
function throwIfNoAzdataOrEulaNotAccepted(): void {
throwIfNoAzdata();
if (!eulaAccepted) {
Logger.log(loc.eulaNotAccepted);
throw new Error(loc.eulaNotAccepted);
}
}
function throwIfNoAzdata() {
if (!localAzdata) {
Logger.log(loc.noAzdata);
throw new Error(loc.noAzdata);
}
}
export function deactivate(): void { }

View File

@@ -56,11 +56,11 @@ export const userRequestedInstall = localize('azdata.userRequestedInstall', "Use
export const userRequestedUpdate = localize('azdata.userRequestedUpdate', "User requested to update Azure Data CLI using 'Azure Data CLI: Check for Update' command");
export const userRequestedAcceptEula = localize('azdata.acceptEula', "User requested to be prompted for accepting EULA by invoking 'Azure Data CLI: Accept EULA' command");
export const updateCheckSkipped = localize('azdata.updateCheckSkipped', "No check for new Azure Data CLI version availability performed as Azure Data CLI was not found to be installed");
export const eulaNotAccepted = localize('azdata.eulaNotAccepted', "Microsoft Privacy statement and Azure Data CLI license terms have not been accepted, [accept the EULA](command:azdata.acceptEula) to use the features that require it.");
export const eulaNotAccepted = localize('azdata.eulaNotAccepted', "Microsoft Privacy statement and Azure Data CLI license terms have not been accepted. Execute the command: [Azure Data CLI: Accept EULA](command:azdata.acceptEula) to accept EULA to enable the features that requires Azure Data CLI.");
export const installManually = (expectedVersion: string, instructionsUrl: string) => localize('azdata.installManually', "Azure Data CLI is not installed. Version: {0} needs to be installed or some features may not work. Please install it manually using these [instructions]({1}). Restart ADS when installation is done.", expectedVersion, instructionsUrl);
export const installCorrectVersionManually = (currentVersion: string, expectedVersion: string, instructionsUrl: string) => localize('azdata.installCorrectVersionManually', "Azure Data CLI version: {0} is installed, version: {1} needs to be installed or some features may not work. Please uninstall the current version and then install the correct version manually using these [instructions]({2}). Restart ADS when installation is done.", currentVersion, expectedVersion, instructionsUrl);
export const promptForEula = (privacyStatementUrl: string, eulaUrl: string) => localize('azdata.promptForEula', "It is required to accept the [Microsoft Privacy Statement]({0}) and the [Azure Data CLI license terms]({1}) to use this extension. Declining this will result in some features not working.", privacyStatementUrl, eulaUrl);
export const promptForEulaLog = (privacyStatementUrl: string, eulaUrl: string) => promptLog(promptForEula(privacyStatementUrl, eulaUrl));
export const userResponseToEulaPrompt = (response: string | undefined) => localize('azdata.promptForEulaResponse', "User response to Eula prompt: {0}", response);
export const eulaAcceptedStateOnStartup = (eulaAccepted: boolean) => localize('azdata.eulaAcceptedStateOnStartup', "eulaAccepted state on startup: {0}", eulaAccepted);
export const eulaAcceptedStateUpdated = (eulaAccepted: boolean) => localize('azdata.eulaAcceptedStateUpdated', "Updated 'eulaAccepted' state to: {0}", eulaAccepted);
export const userResponseToEulaPrompt = (response: string | undefined) => localize('azdata.promptForEulaResponse', "User response to EULA prompt: {0}", response);
export const eulaAcceptedStateOnStartup = (eulaAccepted: boolean) => localize('azdata.eulaAcceptedStateOnStartup', "'EULA Accepted' state on startup: {0}", eulaAccepted);
export const eulaAcceptedStateUpdated = (eulaAccepted: boolean) => localize('azdata.eulaAcceptedStateUpdated', "Updated 'EULA Accepted' state to: {0}", eulaAccepted);

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import { SemVer } from 'semver';
import * as should from 'should';
import * as sinon from 'sinon';
import * as vscode from 'vscode';
@@ -12,10 +11,10 @@ import * as azdata from '../azdata';
import * as childProcess from '../common/childProcess';
import { HttpClient } from '../common/httpClient';
import * as utils from '../common/utils';
import * as loc from '../localizedConstants';
import * as constants from '../constants';
import * as loc from '../localizedConstants';
const oldAzdataMock = <azdata.AzdataTool>{ path: '/path/to/azdata', cachedVersion: new SemVer('0.0.0') };
const oldAzdataMock = new azdata.AzdataTool('/path/to/azdata', '0.0.0');
const releaseJson = {
win32: {
'version': '9999.999.999',

View File

@@ -4,6 +4,8 @@
*--------------------------------------------------------------------------------------------*/
declare module 'azdata-ext' {
import { SemVer } from 'semver';
/**
* Covers defining what the azdata extension exports to other extensions
*
@@ -80,7 +82,7 @@ declare module 'azdata-ext' {
location: string, // "eastus2euap",
resourceGroup: string, // "my-rg",
subscription: string, // "a5082b29-8c6e-4bc5-8ddd-8ef39dfebc39"
},
},
controller: {
'enableBilling': string, // "True"
'logs.rotation.days': string, // "7"
@@ -254,12 +256,20 @@ declare module 'azdata-ext' {
show(name: string): Promise<AzdataOutput<SqlMiShowResult>>
}
}
}
},
getPath(): string,
login(endpoint: string, username: string, password: string): Promise<AzdataOutput<any>>,
/**
* The semVersion corresponding to this installation of azdata. version() method should have been run
* before fetching this value to ensure that correct value is returned. This is almost always correct unless
* Azdata has gotten reinstalled in the background after this IAzdataApi object was constructed.
*/
getSemVersion(): SemVer,
version(): Promise<AzdataOutput<string>>
}
export interface IExtension {
azdata: IAzdataApi;
isEulaAccepted(): boolean;
}
}

View File

@@ -502,18 +502,20 @@
"dependencies": {
"linux-release-info": "^2.0.0",
"promisify-child-process": "^3.1.1",
"semver": "^7.3.2",
"sudo-prompt": "9.1.1",
"vscode-nls": "^4.0.0",
"yamljs": "^0.3.0"
},
"devDependencies": {
"@types/mocha": "^5.2.5",
"@types/semver": "^7.3.1",
"@types/yamljs": "0.2.30",
"mocha": "^5.2.0",
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7",
"should": "^13.2.3",
"typemoq": "^2.1.0",
"vscodetestcover": "^1.1.0",
"should": "^13.2.3"
"vscodetestcover": "^1.1.0"
}
}

View File

@@ -388,6 +388,7 @@ export interface ITool {
finishInitialization(): Promise<void>;
install(): Promise<void>;
isSameOrNewerThan(version: string): boolean;
validateEula(): boolean;
}
export const enum BdcDeploymentType {

View File

@@ -34,3 +34,4 @@ export const optionsNotDefined = (fieldType: FieldType) => localize('optionsNotD
export const optionsNotObjectOrArray = localize('optionsNotObjectOrArray', "FieldInfo.options must be an object if it is not an array");
export const optionsTypeNotFound = localize('optionsTypeNotFound', "When FieldInfo.options is an object it must have 'optionsType' property");
export const optionsTypeRadioOrDropdown = localize('optionsTypeRadioOrDropdown', "When optionsType is not {0} then it must be {1}", OptionsType.Radio, OptionsType.Dropdown);
export const azdataEulaNotAccepted = localize('azdataEulaNotAccepted', "Deployment cannot continue. Azure Data CLI license terms have not been accepted. Execute the command: [Azure Data CLI: Accept EULA] to accept EULA to enable the features that requires Azure Data CLI.");

View File

@@ -3,18 +3,21 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as arc from 'arc';
import * as azdataExt from 'azdata-ext';
import * as azurecore from 'azurecore';
import * as vscode from 'vscode';
import * as arc from 'arc';
export interface IApiService {
readonly azurecoreApi: azurecore.IExtension;
readonly azdataApi: azdataExt.IExtension;
readonly arcApi: arc.IExtension;
}
class ApiService implements IApiService {
constructor() { }
public get azurecoreApi() { return vscode.extensions.getExtension(azurecore.extension.name)?.exports; }
public get azdataApi() { return vscode.extensions.getExtension(azdataExt.extension.name)?.exports; }
public get arcApi() { return vscode.extensions.getExtension(arc.extension.name)?.exports; }
}

View File

@@ -8,10 +8,11 @@ import { SemVer } from 'semver';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { AzdataInstallLocationKey, DeploymentConfigurationKey } from '../../constants';
import { Command, OsDistribution, ToolType } from '../../interfaces';
import { Command, OsDistribution, ToolStatus, ToolType } from '../../interfaces';
import { apiService } from '../apiService';
import { IPlatformService } from '../platformService';
import { dependencyType, ToolBase } from './toolBase';
import { SemVerProxy } from './SemVerProxy';
import * as loc from '../../localizedConstants';
const localize = nls.loadMessageBundle();
export const AzdataToolName = 'azdata';
@@ -44,25 +45,57 @@ export class AzdataTool extends ToolBase {
return 'https://docs.microsoft.com/sql/big-data-cluster/deploy-install-azdata';
}
public validateEula(): boolean {
if (apiService.azdataApi.isEulaAccepted()) {
return true;
} else {
this.setStatusDescription(loc.azdataEulaNotAccepted);
return false;
}
}
/* unused */
protected get versionCommand(): Command {
return {
command: 'azdata -v'
command: ''
};
}
/* unused */
protected get discoveryCommand(): Command {
return {
command: this.discoveryCommandString('azdata')
command: ''
};
}
/**
* updates the version and status for the tool.
*/
protected async updateVersionAndStatus(): Promise<void> {
this.setStatusDescription('');
await this.addInstallationSearchPathsToSystemPath();
const commandOutput = await apiService.azdataApi.azdata.version();
this.version = apiService.azdataApi.azdata.getSemVersion();
if (this.version) {
if (this.autoInstallSupported) {
// set the installationPath
this.setInstallationPathOrAdditionalInformation(apiService.azdataApi.azdata.getPath());
}
this.setStatus(ToolStatus.Installed);
}
else {
this.setInstallationPathOrAdditionalInformation(localize('deployCluster.GetToolVersionErrorInformation', "Error retrieving version information. See output channel '{0}' for more details", this.outputChannelName));
this.setStatusDescription(localize('deployCluster.GetToolVersionError', "Error retrieving version information.{0}Invalid output received, get version command output: '{1}' ", EOL, commandOutput.stderr.join(EOL)));
this.setStatus(ToolStatus.NotInstalled);
}
}
protected getVersionFromOutput(output: string): SemVer | undefined {
let version: SemVer | undefined = undefined;
if (output && output.split(EOL).length > 0) {
version = new SemVerProxy(output.split(EOL)[0].replace(/ /g, ''));
}
return version;
return apiService.azdataApi.azdata.getSemVersion();
}
protected async getSearchPaths(): Promise<string[]> {
switch (this.osDistribution) {
case OsDistribution.win32:

View File

@@ -58,6 +58,8 @@ export abstract class ToolBase implements ITool {
protected abstract readonly versionCommand: Command;
public validateEula(): boolean { return true; }
public get dependencyMessages(): string[] {
return (this.dependenciesByOsType.get(this.osDistribution) || []).map((msgType: dependencyType) => messageByDependencyType.get(msgType)!);
}
@@ -126,10 +128,18 @@ export abstract class ToolBase implements ITool {
return this._statusDescription;
}
protected setStatusDescription(value: string | undefined): void {
this._statusDescription = value;
}
public get installationPathOrAdditionalInformation(): string | undefined {
return this._installationPathOrAdditionalInformation;
}
protected setInstallationPathOrAdditionalInformation(value: string | undefined) {
this._installationPathOrAdditionalInformation = value;
}
protected get installationCommands(): Command[] | undefined {
return this.allInstallationCommands.get(this.osDistribution);
}
@@ -250,7 +260,7 @@ export abstract class ToolBase implements ITool {
/**
* updates the version and status for the tool.
*/
private async updateVersionAndStatus(): Promise<void> {
protected async updateVersionAndStatus(): Promise<void> {
this._statusDescription = '';
await this.addInstallationSearchPathsToSystemPath();
const commandOutput = await this.platformService.runCommand(
@@ -306,7 +316,7 @@ export abstract class ToolBase implements ITool {
}
isSameOrNewerThan(version?: string): boolean {
return !version || (this._version ? SemVerCompare(this._version, version) >= 0 : false);
return !version || (this._version ? SemVerCompare(this._version.raw, version) >= 0 : false);
}
private _pendingVersionAndStatusUpdate!: Promise<void>;

View File

@@ -8,7 +8,6 @@ import assert = require('assert');
import { apiService } from '../services/apiService';
suite('API Service Tests', function (): void {
test('getAzurecoreApi returns azure api', () => {
const api = apiService.azurecoreApi;
assert(api !== undefined);

View File

@@ -7,6 +7,7 @@
/// <reference path='../../../../src/sql/azdata.d.ts'/>
/// <reference path='../../../../src/sql/azdata.proposed.d.ts'/>
/// <reference path='../../../arc/src/typings/arc.d.ts'/>
/// <reference path='../../../azdata/src/typings/azdata-ext.d.ts'/>
/// <reference path='../../../azurecore/src/azurecore.d.ts'/>
/// <reference path='../../../azurecore/src/azureResource/azure-resource.d.ts'/>
/// <reference types='@types/node'/>

View File

@@ -26,6 +26,7 @@ export class ResourceTypePickerDialog extends DialogBase {
private _agreementContainer!: azdata.DivContainer;
private _agreementCheckboxChecked: boolean = false;
private _installToolButton: azdata.window.Button;
private _recheckEulaButton: azdata.window.Button;
private _installationInProgress: boolean = false;
private _tools: ITool[] = [];
@@ -37,10 +38,15 @@ export class ResourceTypePickerDialog extends DialogBase {
super(localize('resourceTypePickerDialog.title', "Select the deployment options"), 'ResourceTypePickerDialog', true);
this._selectedResourceType = defaultResourceType;
this._installToolButton = azdata.window.createButton(localize('deploymentDialog.InstallToolsButton', "Install tools"));
this._recheckEulaButton = azdata.window.createButton(localize('deploymentDialog.RecheckEulaButton', "Validate EULA"));
this._toDispose.push(this._installToolButton.onClick(() => {
this.installTools().catch(error => console.log(error));
}));
this._dialogObject.customButtons = [this._installToolButton];
this._toDispose.push(this._recheckEulaButton.onClick(() => {
this._dialogObject.message = { text: '' }; // clear any previous message.
this._dialogObject.okButton.enabled = this.validateToolsEula(); // re-enable the okButton if validation succeeds.
}));
this._dialogObject.customButtons = [this._installToolButton, this._recheckEulaButton];
this._installToolButton.hidden = true;
this._dialogObject.okButton.label = localize('deploymentDialog.OKButtonText', "Select");
this._dialogObject.okButton.enabled = false; // this is enabled after all tools are discovered.
@@ -278,12 +284,12 @@ export class ResourceTypePickerDialog extends DialogBase {
return [tool.displayName, tool.description, tool.displayStatus, tool.fullVersion || '', toolRequirement.version || '', tool.installationPathOrAdditionalInformation || ''];
});
this._installToolButton.hidden = erroredOrFailedTool || minVersionCheckFailed || (toolsToAutoInstall.length === 0);
this._dialogObject.okButton.enabled = !erroredOrFailedTool && messages.length === 0 && !minVersionCheckFailed && (toolsToAutoInstall.length === 0);
this._dialogObject.okButton.enabled = !erroredOrFailedTool && messages.length === 0 && !minVersionCheckFailed && (toolsToAutoInstall.length === 0) && this.validateToolsEula();
if (messages.length !== 0) {
if (messages.length > 1) {
messages = messages.map(message => `${message}`);
}
messages.push(localize('deploymentDialog.VersionInformationDebugHint', "You will need to restart Azure Data Studio if the tools are installed manually after Azure Data Studio is launched to pick up the updated PATH environment variable. You may find additional details in 'Deployments' output channel"));
messages.push(localize('deploymentDialog.VersionInformationDebugHint', "You will need to restart Azure Data Studio if the tools are installed manually to pick up the change. You may find additional details in 'Deployments' and 'azdata' output channels"));
this._dialogObject.message = {
level: azdata.window.MessageLevel.Error,
text: messages.join(EOL)
@@ -313,6 +319,21 @@ export class ResourceTypePickerDialog extends DialogBase {
this._toolsLoadingComponent.loading = false;
}
private validateToolsEula(): boolean {
const validationSucceeded = this._tools.every(tool => {
const eulaValidated = tool.validateEula();
if (!eulaValidated) {
this._dialogObject.message = {
level: azdata.window.MessageLevel.Error,
text: tool.statusDescription!
};
}
return eulaValidated;
});
this._recheckEulaButton.hidden = validationSucceeded;
return validationSucceeded;
}
private get toolRequirements() {
return this.getCurrentProvider().requiredTools;
}

View File

@@ -194,6 +194,11 @@
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea"
integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==
"@types/semver@^7.3.1":
version "7.3.4"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.4.tgz#43d7168fec6fa0988bb1a513a697b29296721afb"
integrity sha512-+nVsLKlcUCeMzD2ufHEYuJ9a2ovstb6Dp52A5VsoKxDXgvE051XgHI/33I1EymwkRGQkwnA0LkhnUzituGs4EQ==
"@types/yamljs@0.2.30":
version "0.2.30"
resolved "https://registry.yarnpkg.com/@types/yamljs/-/yamljs-0.2.30.tgz#d034e1d329e46e8d0f737c9a8db97f68f81b5382"
@@ -694,6 +699,11 @@ semver@^6.0.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.2:
version "7.3.2"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
should-equal@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-2.0.0.tgz#6072cf83047360867e68e98b09d71143d04ee0c3"