mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -05:00
Add getDockerImageSpec and cleanDockerObjects to API (#19900)
* Add getDockerImageSpec and cleanDockerObjects to API * docs
This commit is contained in:
@@ -73,7 +73,7 @@ export class ProjectsController {
|
||||
private buildHelper: BuildHelper;
|
||||
private buildInfo: DashboardData[] = [];
|
||||
private publishInfo: PublishData[] = [];
|
||||
private deployService: DeployService;
|
||||
public deployService: DeployService;
|
||||
private connectionService: ConnectionService;
|
||||
private azureSqlClient: AzureSqlClient;
|
||||
private autorestHelper: AutorestHelper;
|
||||
|
||||
@@ -12,13 +12,8 @@ import * as vscode from 'vscode';
|
||||
import { ShellExecutionHelper } from '../../tools/shellExecutionHelper';
|
||||
import { AzureSqlClient } from './azureSqlClient';
|
||||
import { ConnectionService } from '../connections/connectionService';
|
||||
import { IDockerSettings, IPublishToDockerSettings } from 'sqldbproj';
|
||||
import { DockerImageSpec, IDockerSettings, IPublishToDockerSettings } from 'sqldbproj';
|
||||
|
||||
interface DockerImageSpec {
|
||||
label: string;
|
||||
containerName: string;
|
||||
tag: string
|
||||
}
|
||||
export class DeployService {
|
||||
|
||||
constructor(private _azureSqlClient = new AzureSqlClient(), private _outputChannel: vscode.OutputChannel, shellExecutionHelper: ShellExecutionHelper | undefined = undefined) {
|
||||
@@ -38,28 +33,6 @@ export class DeployService {
|
||||
}
|
||||
}
|
||||
|
||||
public getDockerImageSpec(projectName: string, baseImage: string, imageUniqueId?: string): DockerImageSpec {
|
||||
|
||||
imageUniqueId = imageUniqueId ?? UUID.generateUuid();
|
||||
// Remove unsupported characters
|
||||
//
|
||||
|
||||
// docker image name and tag can only include letters, digits, underscore, period and dash
|
||||
const regexForDockerImageName = /[^a-zA-Z0-9_,\-]/g;
|
||||
|
||||
let imageProjectName = projectName.replace(regexForDockerImageName, '');
|
||||
const tagMaxLength = 128;
|
||||
const tag = baseImage.replace(':', '-').replace(constants.sqlServerDockerRegistry, '').replace(regexForDockerImageName, '');
|
||||
|
||||
// cut the name if it's too long
|
||||
//
|
||||
imageProjectName = imageProjectName.substring(0, tagMaxLength - (constants.dockerImageNamePrefix.length + tag.length + 2));
|
||||
const imageLabel = `${constants.dockerImageLabelPrefix}-${imageProjectName}`.toLocaleLowerCase();
|
||||
const imageTag = `${constants.dockerImageNamePrefix}-${imageProjectName}-${tag}`.toLocaleLowerCase();
|
||||
const dockerName = `${constants.dockerImageNamePrefix}-${imageProjectName}-${imageUniqueId}`.toLocaleLowerCase();
|
||||
return { label: imageLabel, tag: imageTag, containerName: dockerName };
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Azure Sql server and tries to connect to the new server. If connection fails because of firewall rule, it prompts user to add firewall rule settings
|
||||
* @param profile Azure Sql server settings
|
||||
@@ -99,23 +72,14 @@ export class DeployService {
|
||||
this.logToOutput(constants.dockerImageEulaMessage);
|
||||
this.logToOutput(profile.dockerSettings.dockerBaseImageEula);
|
||||
|
||||
const imageSpec = this.getDockerImageSpec(project.projectFileName, profile.dockerSettings.dockerBaseImage);
|
||||
const imageSpec = getDockerImageSpec(project.projectFileName, profile.dockerSettings.dockerBaseImage);
|
||||
|
||||
// If profile name is not set use the docker name to have a unique name
|
||||
if (!profile.dockerSettings.profileName) {
|
||||
profile.dockerSettings.profileName = imageSpec.containerName;
|
||||
}
|
||||
|
||||
this.logToOutput(constants.cleaningDockerImagesMessage);
|
||||
// Clean up existing docker image
|
||||
const containerIds = await this.getCurrentDockerContainer(imageSpec.label);
|
||||
if (containerIds.length > 0) {
|
||||
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']);
|
||||
}
|
||||
}
|
||||
await this.cleanDockerObjectsIfNeeded(imageSpec.label);
|
||||
|
||||
this.logToOutput(constants.creatingDeploymentSettingsMessage);
|
||||
// Create commands
|
||||
@@ -208,6 +172,23 @@ export class DeployService {
|
||||
return currentIds ? currentIds.split(/\r?\n/) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any containers with the specified label already exist, and if they do prompt the user whether they want to clean them up
|
||||
* @param imageLabel The label of the container to search for
|
||||
*/
|
||||
public async cleanDockerObjectsIfNeeded(imageLabel: string): Promise<void> {
|
||||
this.logToOutput(constants.cleaningDockerImagesMessage);
|
||||
// Clean up existing docker image
|
||||
const containerIds = await this.getCurrentDockerContainer(imageLabel);
|
||||
if (containerIds.length > 0) {
|
||||
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']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async cleanDockerObjects(ids: string[], commandsToClean: string[]): Promise<void> {
|
||||
for (let index = 0; index < ids.length; index++) {
|
||||
const id = ids[index];
|
||||
@@ -220,3 +201,25 @@ export class DeployService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getDockerImageSpec(projectName: string, baseImage: string, imageUniqueId?: string): DockerImageSpec {
|
||||
|
||||
imageUniqueId = imageUniqueId ?? UUID.generateUuid();
|
||||
// Remove unsupported characters
|
||||
//
|
||||
|
||||
// docker image name and tag can only include letters, digits, underscore, period and dash
|
||||
const regexForDockerImageName = /[^a-zA-Z0-9_,\-]/g;
|
||||
|
||||
let imageProjectName = projectName.replace(regexForDockerImageName, '');
|
||||
const tagMaxLength = 128;
|
||||
const tag = baseImage.replace(':', '-').replace(constants.sqlServerDockerRegistry, '').replace(regexForDockerImageName, '');
|
||||
|
||||
// cut the name if it's too long
|
||||
//
|
||||
imageProjectName = imageProjectName.substring(0, tagMaxLength - (constants.dockerImageNamePrefix.length + tag.length + 2));
|
||||
const imageLabel = `${constants.dockerImageLabelPrefix}-${imageProjectName}`.toLocaleLowerCase();
|
||||
const imageTag = `${constants.dockerImageNamePrefix}-${imageProjectName}-${tag}`.toLocaleLowerCase();
|
||||
const dockerName = `${constants.dockerImageNamePrefix}-${imageProjectName}-${imageUniqueId}`.toLocaleLowerCase();
|
||||
return { label: imageLabel, tag: imageTag, containerName: dockerName };
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ProjectsController } from '../controllers/projectController';
|
||||
import { Project } from '../models/project';
|
||||
import { BaseProjectTreeItem } from '../models/tree/baseTreeItem';
|
||||
import { getPublishToDockerSettings } from '../dialogs/deployDatabaseQuickpick';
|
||||
import { getDockerImageSpec } from '../models/deploy/deployService';
|
||||
|
||||
export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvider, sqldbproj.IExtension {
|
||||
constructor(private projectController: ProjectsController) {
|
||||
@@ -223,4 +224,12 @@ export class SqlDatabaseProjectProvider implements dataworkspace.IProjectProvide
|
||||
public getPublishToDockerSettings(project: sqldbproj.ISqlProject): Promise<sqldbproj.IPublishToDockerSettings | undefined> {
|
||||
return getPublishToDockerSettings(project);
|
||||
}
|
||||
|
||||
public getDockerImageSpec(projectName: string, baseImage: string, imageUniqueId?: string): sqldbproj.DockerImageSpec {
|
||||
return getDockerImageSpec(projectName, baseImage, imageUniqueId);
|
||||
}
|
||||
|
||||
public cleanDockerObjectsIfNeeded(imageLabel: string): Promise<void> {
|
||||
return this.projectController.deployService.cleanDockerObjectsIfNeeded(imageLabel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,8 +66,25 @@ declare module 'sqldbproj' {
|
||||
*/
|
||||
addItemPrompt(project: ISqlProject, relativeFilePath: string, options?: AddItemOptions): Promise<void>;
|
||||
|
||||
/**
|
||||
* Gathers information required for publishing a project to a docker container, prompting the user as necessary
|
||||
* @param project The Project being published
|
||||
*/
|
||||
getPublishToDockerSettings(project: ISqlProject): Promise<IPublishToDockerSettings | undefined>;
|
||||
|
||||
/**
|
||||
* Gets the information required to start a docker container for publishing to
|
||||
* @param projectName The name of the project being published
|
||||
* @param baseImage The base docker image being deployed
|
||||
* @param imageUniqueId The unique ID to use in the name, default is a random GUID
|
||||
*/
|
||||
getDockerImageSpec(projectName: string, baseImage: string, imageUniqueId?: string): DockerImageSpec;
|
||||
|
||||
/**
|
||||
* Checks if any containers with the specified label already exist, and if they do prompt the user whether they want to clean them up
|
||||
* @param imageLabel The label of the container to search for
|
||||
*/
|
||||
cleanDockerObjectsIfNeeded(imageLabel: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface AddItemOptions {
|
||||
@@ -333,4 +350,22 @@ declare module 'sqldbproj' {
|
||||
deploymentOptions?: DeploymentOptions;
|
||||
profileUsed?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information for deploying a new docker container
|
||||
*/
|
||||
interface DockerImageSpec {
|
||||
/**
|
||||
* The label to apply to the container
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* The full name to give the container
|
||||
*/
|
||||
containerName: string;
|
||||
/**
|
||||
* The tag to apply to the container
|
||||
*/
|
||||
tag: string
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as should from 'should';
|
||||
import * as sinon from 'sinon';
|
||||
import * as baselines from '../baselines/baselines';
|
||||
import * as testUtils from '../testUtils';
|
||||
import { DeployService } from '../../models/deploy/deployService';
|
||||
import { DeployService, getDockerImageSpec } from '../../models/deploy/deployService';
|
||||
import { Project } from '../../models/project';
|
||||
import * as vscode from 'vscode';
|
||||
import * as azdata from 'azdata';
|
||||
@@ -175,33 +175,31 @@ describe('deploy service', function (): void {
|
||||
});
|
||||
|
||||
it('Should create docker image info correctly', () => {
|
||||
const testContext = createContext();
|
||||
const deployService = new DeployService(testContext.azureSqlClient.object, testContext.outputChannel);
|
||||
const id = UUID.generateUuid().toLocaleLowerCase();
|
||||
const baseImage = 'baseImage:latest';
|
||||
const tag = baseImage.replace(':', '-').replace(constants.sqlServerDockerRegistry, '').replace(/[^a-zA-Z0-9_,\-]/g, '').toLocaleLowerCase();
|
||||
|
||||
should(deployService.getDockerImageSpec('project-name123_test', baseImage, id)).deepEqual({
|
||||
should(getDockerImageSpec('project-name123_test', baseImage, id)).deepEqual({
|
||||
label: `${constants.dockerImageLabelPrefix}-project-name123_test`,
|
||||
containerName: `${constants.dockerImageNamePrefix}-project-name123_test-${id}`,
|
||||
tag: `${constants.dockerImageNamePrefix}-project-name123_test-${tag}`
|
||||
});
|
||||
should(deployService.getDockerImageSpec('project-name1', baseImage, id)).deepEqual({
|
||||
should(getDockerImageSpec('project-name1', baseImage, id)).deepEqual({
|
||||
label: `${constants.dockerImageLabelPrefix}-project-name1`,
|
||||
containerName: `${constants.dockerImageNamePrefix}-project-name1-${id}`,
|
||||
tag: `${constants.dockerImageNamePrefix}-project-name1-${tag}`
|
||||
});
|
||||
should(deployService.getDockerImageSpec('project-name2$#', baseImage, id)).deepEqual({
|
||||
should(getDockerImageSpec('project-name2$#', baseImage, id)).deepEqual({
|
||||
label: `${constants.dockerImageLabelPrefix}-project-name2`,
|
||||
containerName: `${constants.dockerImageNamePrefix}-project-name2-${id}`,
|
||||
tag: `${constants.dockerImageNamePrefix}-project-name2-${tag}`
|
||||
});
|
||||
should(deployService.getDockerImageSpec('project - name3', baseImage, id)).deepEqual({
|
||||
should(getDockerImageSpec('project - name3', baseImage, id)).deepEqual({
|
||||
label: `${constants.dockerImageLabelPrefix}-project-name3`,
|
||||
containerName: `${constants.dockerImageNamePrefix}-project-name3-${id}`,
|
||||
tag: `${constants.dockerImageNamePrefix}-project-name3-${tag}`
|
||||
});
|
||||
should(deployService.getDockerImageSpec('project_name4', baseImage, id)).deepEqual({
|
||||
should(getDockerImageSpec('project_name4', baseImage, id)).deepEqual({
|
||||
label: `${constants.dockerImageLabelPrefix}-project_name4`,
|
||||
containerName: `${constants.dockerImageNamePrefix}-project_name4-${id}`,
|
||||
tag: `${constants.dockerImageNamePrefix}-project_name4-${tag}`
|
||||
@@ -210,7 +208,7 @@ describe('deploy service', function (): void {
|
||||
|
||||
const reallyLongName = new Array(128 + 1).join('a').replace(/[^a-zA-Z0-9_,\-]/g, '');
|
||||
const imageProjectName = reallyLongName.substring(0, 128 - (constants.dockerImageNamePrefix.length + tag.length + 2));
|
||||
should(deployService.getDockerImageSpec(reallyLongName, baseImage, id)).deepEqual({
|
||||
should(getDockerImageSpec(reallyLongName, baseImage, id)).deepEqual({
|
||||
label: `${constants.dockerImageLabelPrefix}-${imageProjectName}`,
|
||||
containerName: `${constants.dockerImageNamePrefix}-${imageProjectName}-${id}`,
|
||||
tag: `${constants.dockerImageNamePrefix}-${imageProjectName}-${tag}`
|
||||
|
||||
Reference in New Issue
Block a user