SQL Proj - Added an option to deploy to docker to select the base image (#17067)

* Added an option to deploy to docker to select the base image
This commit is contained in:
Leila Lali
2021-09-14 13:49:57 -07:00
committed by GitHub
parent 95e82d53e6
commit 423cf8d3d5
5 changed files with 42 additions and 11 deletions

View File

@@ -135,12 +135,13 @@ export const enterPortNumber = localize('enterPortNumber', "Enter port number or
export const enterConnectionStringEnvName = localize('enterConnectionStringEnvName', "Enter connection string environment variable name"); export const enterConnectionStringEnvName = localize('enterConnectionStringEnvName', "Enter connection string environment variable name");
export const enterConnectionStringTemplate = localize('enterConnectionStringTemplate', "Enter connection string template"); export const enterConnectionStringTemplate = localize('enterConnectionStringTemplate', "Enter connection string template");
export const enterPassword = localize('enterPassword', "Enter password"); export const enterPassword = localize('enterPassword', "Enter password");
export const enterBaseImage = localize('enterBaseImage', "Enter the base SQL Server docker image or press enter to use the default value");
export const portMustBeNumber = localize('portMustNotBeNumber', "Port must a be number"); export const portMustBeNumber = localize('portMustNotBeNumber', "Port must a be number");
export const valueCannotBeEmpty = localize('valueCannotBeEmpty', "Value cannot be empty"); export const valueCannotBeEmpty = localize('valueCannotBeEmpty', "Value cannot be empty");
export const dockerImageLabelPrefix = 'source=sqldbproject'; export const dockerImageLabelPrefix = 'source=sqldbproject';
export const dockerImageNamePrefix = 'sqldbproject'; export const dockerImageNamePrefix = 'sqldbproject';
export const connectionNamePrefix = 'SQLDbProject'; export const connectionNamePrefix = 'SQLDbProject';
export const dockerBaseImage = 'mcr.microsoft.com/azure-sql-edge:latest'; export const defaultDockerBaseImage = 'mcr.microsoft.com/mssql/server:2019-latest';
export const commandsFolderName = 'commands'; export const commandsFolderName = 'commands';
export const mssqlFolderName = '.mssql'; export const mssqlFolderName = '.mssql';
export const dockerFileName = 'Dockerfile'; export const dockerFileName = 'Dockerfile';

View File

@@ -104,12 +104,27 @@ export async function launchPublishToDockerContainerQuickpick(project: Project):
return undefined; return undefined;
} }
let baseImage: string | undefined = '';
baseImage = await vscode.window.showInputBox({
title: constants.enterBaseImage,
ignoreFocusOut: true,
value: constants.defaultDockerBaseImage,
validateInput: input => utils.isEmptyString(input) ? constants.valueCannotBeEmpty : undefined
}
);
// Return when user hits escape
if (!baseImage) {
return undefined;
}
localDbSetting = { localDbSetting = {
serverName: 'localhost', serverName: 'localhost',
userName: 'sa', userName: 'sa',
dbName: project.projectFileName, dbName: project.projectFileName,
password: password, password: password,
port: +portNumber, port: +portNumber,
dockerBaseImage: baseImage
}; };
let deploySettings = await getPublishDatabaseSettings(project, false); let deploySettings = await getPublishDatabaseSettings(project, false);

View File

@@ -1,3 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDeploySettings } from '../IDeploySettings'; import { IDeploySettings } from '../IDeploySettings';
export enum AppSettingType { export enum AppSettingType {
@@ -21,4 +26,6 @@ export interface ILocalDbSetting {
userName: string, userName: string,
password: string, password: string,
dbName: string, dbName: string,
dockerBaseImage: string,
connectionRetryTimeout?: number
} }

View File

@@ -20,6 +20,9 @@ export class DeployService {
constructor(private _outputChannel: vscode.OutputChannel) { constructor(private _outputChannel: vscode.OutputChannel) {
} }
private DefaultSqlRetryTimeoutInSec: number = 10;
private DefaultSqlNumberOfRetries: number = 10;
private createConnectionStringTemplate(runtime: string | undefined): string { private createConnectionStringTemplate(runtime: string | undefined): string {
switch (runtime?.toLocaleLowerCase()) { switch (runtime?.toLocaleLowerCase()) {
case 'dotnet': case 'dotnet':
@@ -106,7 +109,7 @@ export class DeployService {
// Create commands // Create commands
// //
await this.createCommands(mssqlFolderPath, commandsFolderPath, dockerFilePath, startFilePath, imageLabel); await this.createCommands(mssqlFolderPath, commandsFolderPath, dockerFilePath, startFilePath, imageLabel, profile.localDbSetting.dockerBaseImage);
this.logToOutput(constants.runningDockerMessage); this.logToOutput(constants.runningDockerMessage);
// Building the image and running the docker // Building the image and running the docker
@@ -144,7 +147,7 @@ export class DeployService {
private async buildAndRunDockerContainer(dockerFilePath: string, imageName: string, root: string, profile: ILocalDbSetting, imageLabel: string): Promise<string | undefined> { private async buildAndRunDockerContainer(dockerFilePath: string, imageName: string, root: string, profile: ILocalDbSetting, imageLabel: string): Promise<string | undefined> {
this.logToOutput('Building docker image ...'); this.logToOutput('Building docker image ...');
await utils.executeCommand(`docker pull ${constants.dockerBaseImage}`, this._outputChannel); await utils.executeCommand(`docker pull ${profile.dockerBaseImage}`, this._outputChannel);
await utils.executeCommand(`docker build -f ${dockerFilePath} -t ${imageName} ${root}`, this._outputChannel); await utils.executeCommand(`docker build -f ${dockerFilePath} -t ${imageName} ${root}`, this._outputChannel);
await utils.executeCommand(`docker images --filter label=${imageLabel}`, this._outputChannel); await utils.executeCommand(`docker images --filter label=${imageLabel}`, this._outputChannel);
@@ -254,7 +257,7 @@ export class DeployService {
return connectionResult ? connectionResult.connectionId : <string>connection; return connectionResult ? connectionResult.connectionId : <string>connection;
} }
public async getConnection(profile: ILocalDbSetting, savePassword: boolean, database: string, timeoutInSeconds: number = 5): Promise<string | undefined> { public async getConnection(profile: ILocalDbSetting, savePassword: boolean, database: string): Promise<string | undefined> {
const getAzdataApi = await utils.getAzdataApi(); const getAzdataApi = await utils.getAzdataApi();
let connection = await utils.retry( let connection = await utils.retry(
constants.connectingToSqlServerOnDockerMessage, constants.connectingToSqlServerOnDockerMessage,
@@ -264,7 +267,7 @@ export class DeployService {
this.validateConnection, this.validateConnection,
this.formatConnectionResult, this.formatConnectionResult,
this._outputChannel, this._outputChannel,
5, timeoutInSeconds); this.DefaultSqlNumberOfRetries, profile.connectionRetryTimeout || this.DefaultSqlRetryTimeoutInSec);
if (connection) { if (connection) {
const connectionResult = <ConnectionResult>connection; const connectionResult = <ConnectionResult>connection;
@@ -311,7 +314,7 @@ export class DeployService {
} }
// Creates command file and docker file needed for deploy operation // Creates command file and docker file needed for deploy operation
private async createCommands(mssqlFolderPath: string, commandsFolderPath: string, dockerFilePath: string, startFilePath: string, imageLabel: string): Promise<void> { private async createCommands(mssqlFolderPath: string, commandsFolderPath: string, dockerFilePath: string, startFilePath: string, imageLabel: string, baseImage: string): Promise<void> {
// Create mssql folders if doesn't exist // Create mssql folders if doesn't exist
// //
await utils.createFolderIfNotExist(mssqlFolderPath); await utils.createFolderIfNotExist(mssqlFolderPath);
@@ -328,7 +331,7 @@ export class DeployService {
// //
await this.createFile(dockerFilePath, await this.createFile(dockerFilePath,
` `
FROM ${constants.dockerBaseImage} FROM ${baseImage}
ENV ACCEPT_EULA=Y ENV ACCEPT_EULA=Y
ENV MSSQL_PID=Developer ENV MSSQL_PID=Developer
LABEL ${imageLabel} LABEL ${imageLabel}

View File

@@ -71,7 +71,9 @@ describe('deploy service', function (): void {
password: 'PLACEHOLDER', password: 'PLACEHOLDER',
port: 1433, port: 1433,
serverName: 'localhost', serverName: 'localhost',
userName: 'sa' userName: 'sa',
dockerBaseImage: 'image',
connectionRetryTimeout: 1
} }
}; };
const projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline); const projFilePath = await testUtils.createTestSqlProjFile(baselines.newProjectFileBaseline);
@@ -93,7 +95,9 @@ describe('deploy service', function (): void {
password: 'PLACEHOLDER', password: 'PLACEHOLDER',
port: 1433, port: 1433,
serverName: 'localhost', serverName: 'localhost',
userName: 'sa' userName: 'sa',
dockerBaseImage: 'image',
connectionRetryTimeout: 1
}; };
const deployService = new DeployService(testContext.outputChannel); const deployService = new DeployService(testContext.outputChannel);
@@ -103,7 +107,7 @@ describe('deploy service', function (): void {
sandbox.stub(azdata.connection, 'getUriForConnection').returns(Promise.resolve('connection')); sandbox.stub(azdata.connection, 'getUriForConnection').returns(Promise.resolve('connection'));
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.getConnection(localDbSettings, false, 'master', 2); let connection = await deployService.getConnection(localDbSettings, false, 'master');
should(connection).equals('connection'); should(connection).equals('connection');
}); });
@@ -138,7 +142,8 @@ describe('deploy service', function (): void {
password: 'PLACEHOLDER', password: 'PLACEHOLDER',
port: 1433, port: 1433,
serverName: 'localhost', serverName: 'localhost',
userName: 'sa' userName: 'sa',
dockerBaseImage: 'image'
} }
}; };