Skip to content

Commit

Permalink
App Service Manage task review comments (#6143)
Browse files Browse the repository at this point in the history
* resolved review comments #1

* resolved review comments #2

* addressed review comments #3

* comment mock tests

* addressed review comments #4

* addressed review comments #5

* resolve build failures

* remove inherit

* remove unreachable codes & Fix few tests

* Fix L0 Tests

* addressed review comments

* changed case

* change source to target

* removed xml2js dependency
  • Loading branch information
vincent1173 authored Jan 5, 2018
1 parent ceaf37f commit 3001125
Show file tree
Hide file tree
Showing 26 changed files with 667 additions and 582 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
"loc.messages.InstallingSiteExtension": "Installing site Extension '%s'",
"loc.messages.FailedToGetResourceID": "Failed to get resource ID for resource type '%s' and resource name '%s'. Error: %s",
"loc.messages.ContinousMonitoringEnabled": "Continuous Monitoring enabled for App Service '%s'.",
"loc.messages.EnablingContinousMonitoring": "Enabling continuous Monitoring for App Service '%s'.",
"loc.messages.MultipleResourceGroupFoundForAppService": "Multiple resource group found for App Service '%s'.",
"loc.messages.StartingContinousWebJobs": "Starting continuous WebJobs",
"loc.messages.StartedContinousWebJobs": "Started continuous WebJobs.",
Expand Down
142 changes: 44 additions & 98 deletions Tasks/AzureAppServiceManage/azureappservicemanage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,53 +8,10 @@ import { AzureApplicationInsights } from 'azure-arm-rest/azure-arm-appinsights';
import { Kudu } from 'azure-arm-rest/azure-arm-app-service-kudu';
import { ApplicationInsightsWebTests } from 'azure-arm-rest/azure-arm-appinsights-webtests';
import { Resources } from 'azure-arm-rest/azure-arm-resource';

const APPLICATION_INSIGHTS_EXTENSION_NAME: string = "Microsoft.ApplicationInsights.AzureWebSites";
const pingApplicationCount: number = 1;
const productionSlot: string = "production";

async function enableContinuousMonitoring(appService: AzureAppService, appInsights: AzureApplicationInsights) {
var appDetails = await appService.get();
var appInsightsResource = await appInsights.get();
var appInsightsWebTests = new ApplicationInsightsWebTests(appInsights.getEndpoint(), appInsights.getResourceGroupName());
var webDeployPublishingProfile = await appService.getWebDeployPublishingProfile();
var applicationUrl = webDeployPublishingProfile.destinationAppUrl;
if(appDetails.kind.indexOf("linux") == -1) {
var appKuduService = await appService.getKuduService();
await appKuduService.installSiteExtension(APPLICATION_INSIGHTS_EXTENSION_NAME);
}

appInsightsResource.tags["hidden-link:" + appDetails.id] = "Resource";
tl.debug('Link app insights with app service via tag');
await appInsights.update(appInsightsResource);
tl.debug('Link app service with app insights via instrumentation key');
await appService.patchApplicationSettings({"APPINSIGHTS_INSTRUMENTATIONKEY": appInsightsResource.properties['InstrumentationKey']});
try {
tl.debug('Enable alwaysOn property for app service.');
await appService.patchConfiguration({"alwaysOn": true});
}
catch(error) {
tl.warning(error);
}

try {
tl.debug('add web test for app service - app insights');
await appInsightsWebTests.addWebTest(appInsightsResource, applicationUrl);
}
catch(error) {
tl.warning(error);
}
}


async function updateDeploymentStatusInKudu(kuduService: Kudu, taskResult: boolean, DeploymentID: string, customMessage: any) {
try {
return await kuduService.updateDeployment(taskResult, DeploymentID, customMessage);
}
catch(error) {
tl.warning(error);
}
}
import { AzureAppServiceUtils } from './operations/AzureAppServiceUtils';
import { KuduServiceUtils } from './operations/KuduServiceUtils';
import { AzureResourceFilterUtils } from './operations/AzureResourceFilterUtils';
import { enableContinuousMonitoring } from './operations/ContinuousMonitoringUtils';

async function run() {
try {
Expand All @@ -76,91 +33,78 @@ async function run() {
var taskResult = true;
var errorMessage: string = "";
var updateDeploymentStatus: boolean = true;

var azureEndpoint: AzureEndpoint = await new AzureRMEndpoint(connectedServiceName).getEndpoint();
var resources: Array<any> = await new Resources(azureEndpoint).getResources('Microsoft.Web/Sites', webAppName);

if(action != "Swap Slots" && !slotName) {
if(!resources || resources.length == 0) {
throw new Error(tl.loc('ResourceDoesntExist', webAppName));
}
else if(resources.length == 1) {
resourceGroupName = resources[0].id.split("/")[4];
}
else {
throw new Error(tl.loc('MultipleResourceGroupFoundForAppService', webAppName));
}
resourceGroupName = await AzureResourceFilterUtils.getResourceGroupName(azureEndpoint, 'Microsoft.Web/Sites', webAppName);
}

tl.debug(`Resource Group: ${resourceGroupName}`);
tl.debug(`Resource Group: ${resourceGroupName}`);
var appService: AzureAppService = new AzureAppService(azureEndpoint, resourceGroupName, webAppName, slotName);
var azureAppServiceUtils: AzureAppServiceUtils = new AzureAppServiceUtils(appService);

switch(action) {
case "Start Azure App Service": {
var appService: AzureAppService = new AzureAppService(azureEndpoint, resourceGroupName, webAppName, slotName);
await appService.start();
await appService.monitorAppState("running");
await appService.pingApplication(pingApplicationCount);
await azureAppServiceUtils.monitorApplicationState("running");
await azureAppServiceUtils.pingApplication();
break;
}
case "Stop Azure App Service": {
var appService: AzureAppService = new AzureAppService(azureEndpoint, resourceGroupName, webAppName, slotName);
await appService.stop();
await appService.monitorAppState("stopped");
await appService.pingApplication(pingApplicationCount);
await azureAppServiceUtils.monitorApplicationState("stopped");
break;
}
case "Restart Azure App Service": {
var appService: AzureAppService = new AzureAppService(azureEndpoint, resourceGroupName, webAppName, slotName);
await appService.restart();
await azureAppServiceUtils.pingApplication();
break;
}
case "Swap Slots": {
targetSlot = (swapWithProduction) ? productionSlot : targetSlot;
targetSlot = (swapWithProduction) ? "production" : targetSlot;
var appServiceSourceSlot: AzureAppService = new AzureAppService(azureEndpoint, resourceGroupName, webAppName, sourceSlot);
var appServiceTargetSlot: AzureAppService = new AzureAppService(azureEndpoint, resourceGroupName, webAppName, targetSlot);
var appServiceSourceSlotUtils: AzureAppServiceUtils = new AzureAppServiceUtils(appServiceSourceSlot);
var appServiceTargetSlotUtils: AzureAppServiceUtils = new AzureAppServiceUtils(appServiceTargetSlot);

if(appServiceSourceSlot.getSlot().toLowerCase() == appServiceTargetSlot.getSlot().toLowerCase()) {
updateDeploymentStatus = false;
throw new Error(tl.loc('SourceAndTargetSlotCannotBeSame'));

}

console.log(tl.loc('WarmingUpSlots'));
await appServiceSourceSlot.pingApplication(1);
await appServiceTargetSlot.pingApplication(1);
try {
await Promise.all([appServiceSourceSlotUtils.pingApplication(), appServiceTargetSlotUtils.pingApplication()]);
}
catch(error) {
tl.debug('Failed to warm-up slots. Error: ' + error);
}

await appServiceSourceSlot.swap(targetSlot, preserveVnet);
break;
}
case "Start all continuous webjobs": {
var appService: AzureAppService = new AzureAppService(azureEndpoint, resourceGroupName, webAppName, slotName);
var appServiceKuduService = await appService.getKuduService();
console.log(tl.loc('StartingContinousWebJobs'));
await appServiceKuduService.startContinuousWebJobs();
console.log(tl.loc('StartedContinousWebJobs'));
var appServiceKuduService: Kudu = await azureAppServiceUtils.getKuduService();
var kuduServiceUtils: KuduServiceUtils = new KuduServiceUtils(appServiceKuduService);
await kuduServiceUtils.startContinuousWebJobs();
break;
}
case "Stop all continuous webjobs": {
var appService: AzureAppService = new AzureAppService(azureEndpoint, resourceGroupName, webAppName, slotName);
var appServiceKuduService = await appService.getKuduService();
console.log(tl.loc('StoppingContinousWebJobs'));
await appServiceKuduService.stopContinuousWebJobs();
console.log(tl.loc('StoppedContinousWebJobs'));
var appServiceKuduService = await azureAppServiceUtils.getKuduService();
var kuduServiceUtils: KuduServiceUtils = new KuduServiceUtils(appServiceKuduService);
await kuduServiceUtils.stopContinuousWebJobs();
break;
}
case "Install Extensions": {
var appService: AzureAppService = new AzureAppService(azureEndpoint, resourceGroupName, webAppName, slotName);
var appServiceKuduService = await appService.getKuduService();
var appServiceKuduService = await azureAppServiceUtils.getKuduService();
var kuduServiceUtils: KuduServiceUtils = new KuduServiceUtils(appServiceKuduService);
var extensionOutputVariablesArray = (extensionOutputVariables) ? extensionOutputVariables.split(',') : [];
await appServiceKuduService.installSiteExtensions(extensionList.split(','), extensionOutputVariablesArray);
await kuduServiceUtils.installSiteExtensions(extensionList.split(','), extensionOutputVariablesArray);
break;
}
case "Enable Continuous Monitoring": {
var appService: AzureAppService = new AzureAppService(azureEndpoint, resourceGroupName, webAppName, slotName);
var appInsights: AzureApplicationInsights = new AzureApplicationInsights(azureEndpoint, appInsightsResourceGroupName, appInsightsResourceName);
try {
await enableContinuousMonitoring(appService, appInsights);
}
catch(error) {
throw new Error(tl.loc('FailedToEnableContinuousMonitoring', error));
}
console.log(tl.loc("ContinousMonitoringEnabled", webAppName));
await enableContinuousMonitoring(azureEndpoint, appService, appInsights);
break;
}
default: {
Expand All @@ -177,22 +121,24 @@ async function run() {
try {
switch(action) {
case "Swap Slots": {
if(appServiceSourceSlot && appServiceTargetSlot && updateDeploymentStatus) {
var sourceSlotKuduService = await appServiceSourceSlot.getKuduService();
var targetSlotKuduService = await appServiceTargetSlot.getKuduService();
if(appServiceSourceSlotUtils && appServiceTargetSlotUtils && updateDeploymentStatus) {
var sourceSlotKuduService = await appServiceSourceSlotUtils.getKuduService();
var targetSlotKuduService = await appServiceTargetSlotUtils.getKuduService();
var sourceSlotKuduServiceUtils = new KuduServiceUtils(sourceSlotKuduService);
var targetSlotKuduServiceUtils = new KuduServiceUtils(targetSlotKuduService);
var customMessage = {
'type': 'SlotSwap',
'sourceSlot': appServiceSourceSlot.getSlot(),
'targetSlot': appServiceTargetSlot.getSlot()
}
var DeploymentID = await updateDeploymentStatusInKudu(sourceSlotKuduService, taskResult, null, customMessage);
await updateDeploymentStatusInKudu(targetSlotKuduService, taskResult, DeploymentID, customMessage);
var DeploymentID = await sourceSlotKuduServiceUtils.updateDeploymentStatus(taskResult, null, customMessage);
await targetSlotKuduServiceUtils.updateDeploymentStatus(taskResult, DeploymentID, customMessage);
}
break;
}
case "Install Extensions": {
if(appServiceKuduService) {
await updateDeploymentStatusInKudu(appServiceKuduService, taskResult, null, {"type": action});
if(kuduServiceUtils) {
await kuduServiceUtils.updateDeploymentStatus(taskResult, null, { "type" : action });
}
break;
}
Expand Down
83 changes: 83 additions & 0 deletions Tasks/AzureAppServiceManage/operations/AzureAppServiceUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import tl = require('vsts-task-lib/task');
import { AzureAppService } from 'azure-arm-rest/azure-arm-app-service';
import webClient = require('azure-arm-rest/webClient');
var parseString = require('xml2js').parseString;
import Q = require('q');
import { Kudu } from 'azure-arm-rest/azure-arm-app-service-kudu';

export class AzureAppServiceUtils {
private _appService: AzureAppService;
constructor(appService: AzureAppService) {
this._appService = appService;
}

public async monitorApplicationState(state: string): Promise<void> {
state = state.toLowerCase();
if(["running", "stopped"].indexOf(state) == -1) {
throw new Error(tl.loc('InvalidMonitorAppState', state));
}

while(true) {
var appDetails = await this._appService.get(true);
if(appDetails && appDetails.properties && appDetails.properties["state"]) {
tl.debug(`App Service state: ${appDetails.properties["state"]}`)
if(appDetails.properties["state"].toLowerCase() == state) {
tl.debug(`App Service state '${appDetails.properties["state"]}' matched with expected state '${state}'.`);
console.log(tl.loc('AppServiceState', appDetails.properties["state"]));
break;
}
await webClient.sleepFor(5);
}
else {
tl.debug('Unable to monitor app service details as the state is unknown.');
break;
}
}
}

public async getWebDeployPublishingProfile(): Promise<any> {
var publishingProfile = await this._appService.getPublishingProfileWithSecrets();
var defer = Q.defer<any>();
parseString(publishingProfile, (error, result) => {
for (var index in result.publishData.publishProfile) {
if (result.publishData.publishProfile[index].$.publishMethod === "MSDeploy") {
defer.resolve(result.publishData.publishProfile[index].$);
}
}
defer.reject(tl.loc('ErrorNoSuchDeployingMethodExists'));
});

return defer.promise;
}

public async pingApplication(): Promise<void> {
try {
var applicationUrl: string = (await this.getWebDeployPublishingProfile()).destinationAppUrl;

if(!applicationUrl) {
tl.debug('Application Url not found.');
return;
}
var webRequest = new webClient.WebRequest();
webRequest.method = 'GET';
webRequest.uri = applicationUrl;
tl.debug('pausing for 5 seconds before request');
await webClient.sleepFor(5);
var response = await webClient.sendRequest(webRequest);
tl.debug(`App Service status Code: '${response.statusCode}'. Status Message: '${response.statusMessage}'`);
}
catch(error) {
tl.debug(`Unable to ping App Service. Error: ${error}`);
}
}

public async getKuduService(): Promise<Kudu> {
var publishingCredentials = await this._appService.getPublishingCredentials();
if(publishingCredentials.properties["scmUri"]) {
tl.setVariable(`AZURE_APP_SERVICE_KUDU_${this._appService.getSlot()}_PASSWORD`, publishingCredentials.properties["publishingPassword"], true);
return new Kudu(publishingCredentials.properties["scmUri"], publishingCredentials.properties["publishingUserName"], publishingCredentials.properties["publishingPassword"]);
}

throw Error(tl.loc('KuduSCMDetailsAreEmpty'));
}
}
Loading

0 comments on commit 3001125

Please sign in to comment.