sqlproj publish to container - october release improvements (#17289)

This commit is contained in:
Leila Lali
2021-10-07 12:55:43 -07:00
committed by GitHub
parent 04ee2c5d83
commit f875699cd2
5 changed files with 67 additions and 29 deletions

View File

@@ -160,7 +160,8 @@ export const enterConnStringTemplateDescription = localize('enterConnStringTempl
export const appSettingPrompt = localize('appSettingPrompt', "Would you like to update Azure Function local.settings.json with the new connection string?"); export const appSettingPrompt = localize('appSettingPrompt', "Would you like to update Azure Function local.settings.json with the new connection string?");
export const enterConnectionStringEnvNameDescription = localize('enterConnectionStringEnvNameDescription', "Enter environment variable for SQL connection string"); export const enterConnectionStringEnvNameDescription = localize('enterConnectionStringEnvNameDescription', "Enter environment variable for SQL connection string");
export const deployDbTaskName = localize('deployDbTaskName', "Deploying SQL Db Project Locally"); export const deployDbTaskName = localize('deployDbTaskName', "Deploying SQL Db Project Locally");
export const deployProjectSucceed = localize('deployProjectSucceed', "Database project deployed successfully"); export const publishProjectSucceed = localize('publishProjectSucceed', "Database project published successfully");
export const publishingProjectMessage = localize('publishingProjectMessage', "Publishing project in a container...");
export const cleaningDockerImagesMessage = localize('cleaningDockerImagesMessage', "Cleaning existing deployments..."); export const cleaningDockerImagesMessage = localize('cleaningDockerImagesMessage', "Cleaning existing deployments...");
export const creatingDeploymentSettingsMessage = localize('creatingDeploymentSettingsMessage', "Creating deployment settings ..."); export const creatingDeploymentSettingsMessage = localize('creatingDeploymentSettingsMessage', "Creating deployment settings ...");
export const runningDockerMessage = localize('runningDockerMessage', "Building and running the docker container ..."); export const runningDockerMessage = localize('runningDockerMessage', "Building and running the docker container ...");
@@ -169,8 +170,10 @@ export const dockerContainerNotRunningErrorMessage = localize('dockerContainerNo
export const dockerContainerFailedToRunErrorMessage = localize('dockerContainerFailedToRunErrorMessage', "Failed to run the docker container"); export const dockerContainerFailedToRunErrorMessage = localize('dockerContainerFailedToRunErrorMessage', "Failed to run the docker container");
export const connectingToSqlServerOnDockerMessage = localize('connectingToSqlServerOnDockerMessage', "Connecting to SQL Server on Docker"); export const connectingToSqlServerOnDockerMessage = localize('connectingToSqlServerOnDockerMessage', "Connecting to SQL Server on Docker");
export const deployProjectFailedMessage = localize('deployProjectFailedMessage', "Failed to open a connection to the deployed database'"); export const deployProjectFailedMessage = localize('deployProjectFailedMessage', "Failed to open a connection to the deployed database'");
export const containerAlreadyExistForProject = localize('containerAlreadyExistForProject', "Other servers on container already exist for the project. Do you want to delete them?'");
export const checkoutOutputMessage = localize('checkoutOutputMessage', "Check output pane for more details.");
export function taskFailedError(taskName: string, err: string): string { return localize('taskFailedError.error', "Failed to complete task '{0}'. Error: {1}", taskName, err); } export function taskFailedError(taskName: string, err: string): string { return localize('taskFailedError.error', "Failed to complete task '{0}'. Error: {1}", taskName, err); }
export function publishToContainerFailed(errorMessage: string) { return localize('publishToContainerFailed', "Failed to publish to container. Check output pane for more details. {0}", errorMessage); } export function publishToContainerFailed(errorMessage: string) { return localize('publishToContainerFailed', "Failed to publish to container. {0}", errorMessage); }
export function deployAppSettingUpdateFailed(appSetting: string) { return localize('deployAppSettingUpdateFailed', "Failed to update app setting '{0}'", appSetting); } export function deployAppSettingUpdateFailed(appSetting: string) { return localize('deployAppSettingUpdateFailed', "Failed to update app setting '{0}'", appSetting); }
export function deployAppSettingUpdating(appSetting: string) { return localize('deployAppSettingUpdating', "Updating app setting: '{0}'", appSetting); } export function deployAppSettingUpdating(appSetting: string) { return localize('deployAppSettingUpdating', "Updating app setting: '{0}'", appSetting); }
export function connectionFailedError(error: string) { return localize('connectionFailedError', "Connection failed error: '{0}'", error); } export function connectionFailedError(error: string) { return localize('connectionFailedError', "Connection failed error: '{0}'", error); }

View File

@@ -437,6 +437,12 @@ export async function executeCommand(cmd: string, outputChannel: vscode.OutputCh
timeout: timeout timeout: timeout
}, (err, stdout) => { }, (err, stdout) => {
if (err) { if (err) {
// removing sensitive data from the exception
sensitiveData.forEach(element => {
err.cmd = err.cmd?.replace(element, '***');
err.message = err.message?.replace(element, '***');
});
reject(err); reject(err);
} else { } else {
resolve(stdout); resolve(stdout);
@@ -552,3 +558,18 @@ export function isValidSQLPassword(password: string, userName: string = 'sa'): b
const hasNonAlphas = /\W/.test(password) ? 1 : 0; const hasNonAlphas = /\W/.test(password) ? 1 : 0;
return !containsUserName && password.length >= 8 && password.length <= 128 && (hasUpperCase + hasLowerCase + hasNumbers + hasNonAlphas >= 3); return !containsUserName && password.length >= 8 && password.length <= 128 && (hasUpperCase + hasLowerCase + hasNumbers + hasNonAlphas >= 3);
} }
export async function showErrorMessageWithOutputChannel(errorMessageFunc: (error: string) => string, error: any, outputChannel: vscode.OutputChannel): Promise<void> {
const result = await vscode.window.showErrorMessage(errorMessageFunc(getErrorMessage(error)), constants.checkoutOutputMessage);
if (result !== undefined) {
outputChannel.show();
}
}
export async function showInfoMessageWithOutputChannel(message: string, outputChannel: vscode.OutputChannel): Promise<void> {
const result = await vscode.window.showInformationMessage(message, constants.checkoutOutputMessage);
if (result !== undefined) {
outputChannel.show();
}
}

View File

@@ -73,11 +73,11 @@ export class ProjectsController {
projFileWatchers = new Map<string, vscode.FileSystemWatcher>(); projFileWatchers = new Map<string, vscode.FileSystemWatcher>();
constructor(outputChannel: vscode.OutputChannel) { constructor(private _outputChannel: vscode.OutputChannel) {
this.netCoreTool = new NetCoreTool(outputChannel); this.netCoreTool = new NetCoreTool(this._outputChannel);
this.buildHelper = new BuildHelper(); this.buildHelper = new BuildHelper();
this.deployService = new DeployService(outputChannel); this.deployService = new DeployService(this._outputChannel);
this.autorestHelper = new AutorestHelper(outputChannel); this.autorestHelper = new AutorestHelper(this._outputChannel);
} }
public getDashboardPublishData(projectFile: string): (string | dataworkspace.IconCellValue)[][] { public getDashboardPublishData(projectFile: string): (string | dataworkspace.IconCellValue)[][] {
@@ -270,6 +270,7 @@ export class ProjectsController {
if (deployProfile && deployProfile.deploySettings) { if (deployProfile && deployProfile.deploySettings) {
let connectionUri: string | undefined; let connectionUri: string | undefined;
if (deployProfile.localDbSetting) { if (deployProfile.localDbSetting) {
void utils.showInfoMessageWithOutputChannel(constants.publishingProjectMessage, this._outputChannel);
connectionUri = await this.deployService.deploy(deployProfile, project); connectionUri = await this.deployService.deploy(deployProfile, project);
if (connectionUri) { if (connectionUri) {
deployProfile.deploySettings.connectionUri = connectionUri; deployProfile.deploySettings.connectionUri = connectionUri;
@@ -281,16 +282,16 @@ export class ProjectsController {
if (deployProfile.localDbSetting) { if (deployProfile.localDbSetting) {
await this.deployService.getConnection(deployProfile.localDbSetting, true, deployProfile.localDbSetting.dbName); await this.deployService.getConnection(deployProfile.localDbSetting, true, deployProfile.localDbSetting.dbName);
} }
void vscode.window.showInformationMessage(constants.deployProjectSucceed); void vscode.window.showInformationMessage(constants.publishProjectSucceed);
} else { } else {
void vscode.window.showErrorMessage(constants.publishToContainerFailed(publishResult?.errorMessage || '')); void utils.showErrorMessageWithOutputChannel(constants.publishToContainerFailed, publishResult?.errorMessage || '', this._outputChannel);
} }
} else { } else {
void vscode.window.showErrorMessage(constants.publishToContainerFailed(constants.deployProjectFailedMessage)); void utils.showErrorMessageWithOutputChannel(constants.publishToContainerFailed, constants.deployProjectFailedMessage, this._outputChannel);
} }
} }
} catch (error) { } catch (error) {
void vscode.window.showErrorMessage(constants.publishToContainerFailed(utils.getErrorMessage(error))); void utils.showErrorMessageWithOutputChannel(constants.publishToContainerFailed, error, this._outputChannel);
} }
return; return;
} }

View File

@@ -111,11 +111,15 @@ export class DeployService {
const dockerFilePath = path.join(mssqlFolderPath, constants.dockerFileName); const dockerFilePath = path.join(mssqlFolderPath, constants.dockerFileName);
const startFilePath = path.join(commandsFolderPath, constants.startCommandName); const startFilePath = path.join(commandsFolderPath, constants.startCommandName);
this.logToOutput(constants.cleaningDockerImagesMessage);
// Clean up existing docker image // Clean up existing docker image
const containerIds = await this.getCurrentDockerContainer(imageLabel);
await this.cleanDockerObjects(`docker ps -q -a --filter label=${imageLabel}`, ['docker stop', 'docker rm']); if (containerIds && containerIds.length > 0) {
await this.cleanDockerObjects(`docker images -f label=${imageLabel} -q`, [`docker rmi -f `]); const result = await vscode.window.showWarningMessage(constants.containerAlreadyExistForProject, constants.yesString, constants.noString);
if (result === constants.yesString) {
this.logToOutput(constants.cleaningDockerImagesMessage);
await this.cleanDockerObjects(containerIds, ['docker stop', 'docker rm']);
}
}
this.logToOutput(constants.creatingDeploymentSettingsMessage); this.logToOutput(constants.creatingDeploymentSettingsMessage);
// Create commands // Create commands
@@ -363,17 +367,22 @@ RUN ["/bin/bash", "/opt/commands/start.sh"]
await fse.writeFile(filePath, content); await fse.writeFile(filePath, content);
} }
public async cleanDockerObjects(commandToGetObjects: string, commandsToClean: string[]): Promise<void> { private async getCurrentIds(commandToRun: string): Promise<string[]> {
const currentIds = await utils.executeCommand(commandToGetObjects, this._outputChannel); const currentIds = await utils.executeCommand(commandToRun, this._outputChannel);
if (currentIds) { return currentIds ? currentIds.split(/\r?\n/) : [];
const ids = currentIds.split(/\r?\n/); }
for (let index = 0; index < ids.length; index++) {
const id = ids[index]; public async getCurrentDockerContainer(imageLabel: string): Promise<string[]> {
if (id) { return await this.getCurrentIds(`docker ps -q -a --filter label=${imageLabel}`);
for (let commandId = 0; commandId < commandsToClean.length; commandId++) { }
const command = commandsToClean[commandId];
await utils.executeCommand(`${command} ${id}`, this._outputChannel); public async cleanDockerObjects(ids: string[], commandsToClean: string[]): Promise<void> {
} for (let index = 0; index < ids.length; index++) {
const id = ids[index];
if (id) {
for (let commandId = 0; commandId < commandsToClean.length; commandId++) {
const command = commandsToClean[commandId];
await utils.executeCommand(`${command} ${id}`, this._outputChannel);
} }
} }
} }

View File

@@ -15,6 +15,7 @@ import * as childProcess from 'child_process';
import { AppSettingType, IDeployProfile } from '../../models/deploy/deployProfile'; import { AppSettingType, IDeployProfile } from '../../models/deploy/deployProfile';
let fse = require('fs-extra'); let fse = require('fs-extra');
let path = require('path'); let path = require('path');
import * as constants from '../../common/constants';
export interface TestContext { export interface TestContext {
outputChannel: vscode.OutputChannel; outputChannel: vscode.OutputChannel;
@@ -81,6 +82,7 @@ describe('deploy service', function (): void {
const deployService = new DeployService(testContext.outputChannel); const deployService = new DeployService(testContext.outputChannel);
sandbox.stub(azdata.connection, 'connect').returns(Promise.resolve(mockConnectionResult)); sandbox.stub(azdata.connection, 'connect').returns(Promise.resolve(mockConnectionResult));
sandbox.stub(azdata.connection, 'getUriForConnection').returns(Promise.resolve('connection')); sandbox.stub(azdata.connection, 'getUriForConnection').returns(Promise.resolve('connection'));
sandbox.stub(vscode.window, 'showWarningMessage').returns(<any>Promise.resolve(constants.yesString));
sandbox.stub(azdata.tasks, 'startBackgroundOperation').callThrough(); sandbox.stub(azdata.tasks, 'startBackgroundOperation').callThrough();
sandbox.stub(childProcess, 'exec').yields(undefined, 'id'); sandbox.stub(childProcess, 'exec').yields(undefined, 'id');
let connection = await deployService.deploy(deployProfile, project1); let connection = await deployService.deploy(deployProfile, project1);
@@ -168,10 +170,12 @@ describe('deploy service', function (): void {
} }
}; };
const appInteg = {appSettingType: AppSettingType.AzureFunction, const appInteg = {
appSettingType: AppSettingType.AzureFunction,
appSettingFile: filePath, appSettingFile: filePath,
deploySettings: undefined, deploySettings: undefined,
envVariableName: 'SQLConnectionString'}; envVariableName: 'SQLConnectionString'
};
const deployService = new DeployService(testContext.outputChannel); const deployService = new DeployService(testContext.outputChannel);
sandbox.stub(childProcess, 'exec').yields(undefined, 'id'); sandbox.stub(childProcess, 'exec').yields(undefined, 'id');
@@ -240,8 +244,8 @@ describe('deploy service', function (): void {
id2 id2
id3`); id3`);
await deployService.cleanDockerObjects(`docker ps -q -a --filter label=test`, ['docker stop', 'docker rm']); const ids = await deployService.getCurrentDockerContainer('label');
await deployService.cleanDockerObjects(ids, ['docker stop', 'docker rm']);
should(process.calledThrice); should(process.calledThrice);
}); });
}); });