SQL Database Project - Deploy db to docker (#16406)

Added a new command to deploy the project to docker
This commit is contained in:
Leila Lali
2021-08-12 13:24:16 -07:00
committed by GitHub
parent 57e11c7b5c
commit 32a71a2de6
18 changed files with 1111 additions and 92 deletions

View File

@@ -126,6 +126,53 @@ export const selectDatabase = localize('selectDatabase', "Select database");
export const done = localize('done', "Done");
export const nameMustNotBeEmpty = localize('nameMustNotBeEmpty', "Name must not be empty");
// Deploy
export const selectDeployOption = localize('selectDeployOption', "Select where to deploy the project to");
export const deployToExistingServer = localize('deployToExistingServer', "Deploy to existing server");
export const deployToDockerContainer = localize('deployToDockerContainer', "Deploy to docker container");
export const enterPortNumber = localize('enterPortNumber', "Enter port number or press enter to use the default value");
export const enterConnectionStringEnvName = localize('enterConnectionStringEnvName', "Enter connection string environment variable name");
export const enterConnectionStringTemplate = localize('enterConnectionStringTemplate', "Enter connection string template");
export const enterPassword = localize('enterPassword', "Enter password or press enter to use the generated password");
export const portMustBeNumber = localize('portMustNotBeNumber', "Port must a be number");
export const valueCannotBeEmpty = localize('valueCannotBeEmpty', "Value cannot be empty");
export const dockerImageLabelPrefix = 'source=sqldbproject';
export const dockerImageNamePrefix = 'sqldbproject';
export const connectionNamePrefix = 'SQLDbProject';
export const dockerBaseImage = 'mcr.microsoft.com/azure-sql-edge:latest';
export const commandsFolderName = 'commands';
export const mssqlFolderName = '.mssql';
export const dockerFileName = 'Dockerfile';
export const startCommandName = 'start.sh';
export const defaultPortNumber = '1433';
export const defaultConnectionStringEnvVarName = 'SQLConnectionString';
export const defaultConnectionStringTemplate = 'Data Source=@@SERVER@@,@@PORT@@;Initial Catalog=@@DATABASE@@;User id=@@USER@@;Password=@@SA_PASSWORD@@;';
export const azureFunctionLocalSettingsFileName = 'local.settings.json';
export const enterConnStringTemplateDescription = localize('enterConnStringTemplateDescription', "Enter a template for SQL 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 deployDbTaskName = localize('deployDbTaskName', "Deploying SQL Db Project Locally");
export const deployProjectSucceed = localize('deployProjectSucceed', "Database project deployed successfully");
export const cleaningDockerImagesMessage = localize('cleaningDockerImagesMessage', "Cleaning existing deployments...");
export const creatingDeploymentSettingsMessage = localize('creatingDeploymentSettingsMessage', "Creating deployment settings ...");
export const runningDockerMessage = localize('runningDockerMessage', "Building and running the docker container ...");
export const dockerContainerNotRunningErrorMessage = localize('dockerContainerNotRunningErrorMessage', "Docker container is not running");
export const dockerContainerFailedToRunErrorMessage = localize('dockerContainerFailedToRunErrorMessage', "Failed to run the docker container");
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 function taskFailedError(taskName: string, err: string): string { return localize('taskFailedError.error', "Failed to complete task '{0}'. Error: {1}", taskName, err); }
export function deployProjectFailed(errorMessage: string) { return localize('deployProjectFailed', "Failed to deploy project. Check output pane for more details. {0}", errorMessage); }
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 connectionFailedError(error: string) { return localize('connectionFailedError', "Connection failed error: '{0}'", error); }
export function dockerContainerCreatedMessage(id: string) { return localize('dockerContainerCreatedMessage', "Docker created id: '{0}'", id); }
export function dockerLogMessage(log: string) { return localize('dockerLogMessage', "Docker logs: '{0}'", log); }
export function retryWaitMessage(numberOfSeconds: number, name: string) { return localize('retryWaitMessage', "Waiting for {0} seconds before another attempt for operation '{1}'", numberOfSeconds, name); }
export function retryRunMessage(attemptNumber: number, numberOfAttempts: number, name: string) { return localize('retryRunMessage', "Running operation '{2}' Attempt {0} of {1}", attemptNumber, numberOfAttempts, name); }
export function retrySucceedMessage(name: string, result: string) { return localize('retrySucceedMessage', "Operation '{0}' completed successfully. Result: {1}", name, result); }
export function retryFailedMessage(name: string, result: string, error: string) { return localize('retryFailedMessage', "Operation '{0}' failed. Re-trying... Current Result: {1}. Error: '{2}'", name, result, error); }
export function retryMessage(name: string, error: string) { return localize('retryMessage', "Operation '{0}' failed. Re-trying... Error: '{1}'", name, error || ''); }
// Add Database Reference dialog strings
export const addDatabaseReferenceDialogName = localize('addDatabaseReferencedialogName', "Add database reference");

View File

@@ -14,6 +14,13 @@ import * as mssql from '../../../mssql';
import * as vscodeMssql from 'vscode-mssql';
import { promises as fs } from 'fs';
import { Project } from '../models/project';
import * as childProcess from 'child_process';
import * as fse from 'fs-extra';
export interface ValidationResult {
errorMessage: string;
validated: boolean
}
/**
* Consolidates on the error message string
@@ -395,3 +402,79 @@ try {
export function getAzdataApi(): typeof azdataType | undefined {
return azdataApi;
}
export async function createFolderIfNotExist(folderPath: string): Promise<void> {
try {
await fse.mkdir(folderPath);
} catch {
// Ignore if failed
}
}
export async function executeCommand(cmd: string, outputChannel: vscode.OutputChannel, timeout: number = 5 * 60 * 1000): Promise<string> {
return new Promise<string>((resolve, reject) => {
if (outputChannel) {
outputChannel.appendLine(` > ${cmd}`);
}
let child = childProcess.exec(cmd, {
timeout: timeout
}, (err, stdout) => {
if (err) {
reject(err);
} else {
resolve(stdout);
}
});
// Add listeners to print stdout and stderr if an output channel was provided
if (child?.stdout) {
child.stdout.on('data', data => { outputDataChunk(outputChannel, data, ' stdout: '); });
}
if (child?.stderr) {
child.stderr.on('data', data => { outputDataChunk(outputChannel, data, ' stderr: '); });
}
});
}
export function outputDataChunk(outputChannel: vscode.OutputChannel, data: string | Buffer, header: string): void {
data.toString().split(/\r?\n/)
.forEach(line => {
if (outputChannel) {
outputChannel.appendLine(header + line);
}
});
}
export async function retry<T>(
name: string,
attempt: () => Promise<T>,
verify: (result: T) => Promise<ValidationResult>,
formatResult: (result: T) => Promise<string>,
outputChannel: vscode.OutputChannel,
numberOfAttempts: number = 10,
waitInSeconds: number = 2
): Promise<T | undefined> {
for (let count = 0; count < numberOfAttempts; count++) {
outputChannel.appendLine(constants.retryWaitMessage(waitInSeconds, name));
await new Promise(c => setTimeout(c, waitInSeconds * 1000));
outputChannel.appendLine(constants.retryRunMessage(count, numberOfAttempts, name));
try {
let result = await attempt();
const validationResult = await verify(result);
const formattedResult = await formatResult(result);
if (validationResult.validated) {
outputChannel.appendLine(constants.retrySucceedMessage(name, formattedResult));
return result;
} else {
outputChannel.appendLine(constants.retryFailedMessage(name, formattedResult, validationResult.errorMessage));
}
} catch (err) {
outputChannel.appendLine(constants.retryMessage(name, err));
}
}
return undefined;
}