diff --git a/Tasks/AzureAppServiceManage/Strings/resources.resjson/en-US/resources.resjson b/Tasks/AzureAppServiceManage/Strings/resources.resjson/en-US/resources.resjson index c70bf043110b..5ddf03cc5682 100644 --- a/Tasks/AzureAppServiceManage/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/AzureAppServiceManage/Strings/resources.resjson/en-US/resources.resjson @@ -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.", diff --git a/Tasks/AzureAppServiceManage/azureappservicemanage.ts b/Tasks/AzureAppServiceManage/azureappservicemanage.ts index e5f6288b0837..2cf98264ee11 100644 --- a/Tasks/AzureAppServiceManage/azureappservicemanage.ts +++ b/Tasks/AzureAppServiceManage/azureappservicemanage.ts @@ -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 { @@ -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 = 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: { @@ -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; } diff --git a/Tasks/AzureAppServiceManage/operations/AzureAppServiceUtils.ts b/Tasks/AzureAppServiceManage/operations/AzureAppServiceUtils.ts new file mode 100644 index 000000000000..136b01dc68be --- /dev/null +++ b/Tasks/AzureAppServiceManage/operations/AzureAppServiceUtils.ts @@ -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 { + 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 { + var publishingProfile = await this._appService.getPublishingProfileWithSecrets(); + var defer = Q.defer(); + 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 { + 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 { + 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')); + } +} \ No newline at end of file diff --git a/Tasks/AzureAppServiceManage/operations/AzureApplicationInsightsWebTestsUtils.ts b/Tasks/AzureAppServiceManage/operations/AzureApplicationInsightsWebTestsUtils.ts new file mode 100644 index 000000000000..14f6c61670fb --- /dev/null +++ b/Tasks/AzureAppServiceManage/operations/AzureApplicationInsightsWebTestsUtils.ts @@ -0,0 +1,92 @@ +import tl = require('vsts-task-lib/task'); +import { AzureEndpoint, WebTest } from 'azure-arm-rest/azureModels'; +import { ApplicationInsightsWebTests } from 'azure-arm-rest/azure-arm-appinsights-webtests'; + +export class AzureApplicationInsightsWebTestsUtils { + + constructor(applicationInsightsWebTests: ApplicationInsightsWebTests) { + this._applicationInsightsWebTests = applicationInsightsWebTests; + } + + public async addWebTest(appInsightsResource: any, applicationUrl: string, testName?: string): Promise { + let webTests = await this._applicationInsightsWebTests.list(); + + for(let webTest of webTests) { + let isTagPresent: boolean = false; + let isApplicationUrlPresent: boolean = false; + for(let tag in webTest.tags) { + if(tag.toLowerCase().indexOf(appInsightsResource.id.toLowerCase()) != -1) { + isTagPresent = true; + break; + } + } + + isApplicationUrlPresent = webTest.properties['Configuration'].WebTest.toLowerCase().indexOf(applicationUrl.toLowerCase()) != -1; + if(isTagPresent && isApplicationUrlPresent) { + console.log(tl.loc('WebTestAlreadyConfigured', applicationUrl)); + return; + } + } + + await this.create(appInsightsResource, applicationUrl, testName); + } + + public async create(appInsightsResource: any, applicationUrl: string, testName?: string): Promise { + let webTestData: WebTest = this.configureNewWebTest(appInsightsResource, applicationUrl, testName); + await this._applicationInsightsWebTests.create(webTestData); + } + + public configureNewWebTest(appInsightsResource: any, applicationUrl: string, testName?: string): WebTest { + let webTestName = testName ? testName: "vsts-web-test-" + Date.now(); + let webTestData = JSON.parse(JSON.stringify(this._webTestData)); + webTestData.name = webTestName; + webTestData.properties.Name = webTestName; + webTestData.properties.SyntheticMonitorId = webTestName; + webTestData.location = appInsightsResource.location; + webTestData.tags["hidden-link:" + appInsightsResource.id] = "Resource"; + webTestData.properties.Configuration.WebTest = webTestData.properties.Configuration.WebTest.replace("{WEB_TEST_NAME}", webTestName); + webTestData.properties.Configuration.WebTest = webTestData.properties.Configuration.WebTest.replace("{APPLICATION_URL}", applicationUrl); + + return webTestData; + } + + private _applicationInsightsWebTests: ApplicationInsightsWebTests; + private _webTestData: WebTest = { + "name": "", + "location": "", + "tags": {}, + "type": "microsoft.insights/webtests", + "properties": { + "SyntheticMonitorId": "", + "Name": "", + "Description": "", + "Enabled": true, + "Frequency": 300, + "Timeout": 120, + "Kind": "ping", + "RetryEnabled": true, + "Locations": [ + { + "Id": "us-tx-sn1-azr" + }, + { + "Id": "us-il-ch1-azr" + }, + { + "Id": "us-ca-sjc-azr" + }, + { + "Id": "us-va-ash-azr" + }, + { + "Id": "us-fl-mia-edge" + } + ], + "Configuration": { + "WebTest": " " + } + } + } + + +} \ No newline at end of file diff --git a/Tasks/AzureAppServiceManage/operations/AzureResourceFilterUtils.ts b/Tasks/AzureAppServiceManage/operations/AzureResourceFilterUtils.ts new file mode 100644 index 000000000000..93638d78e5e3 --- /dev/null +++ b/Tasks/AzureAppServiceManage/operations/AzureResourceFilterUtils.ts @@ -0,0 +1,22 @@ +import tl = require('vsts-task-lib/task'); +import { AzureEndpoint } from 'azure-arm-rest/azureModels'; +import { Resources } from 'azure-arm-rest/azure-arm-resource'; + +export class AzureResourceFilterUtils { + public static async getResourceGroupName(endpoint: AzureEndpoint, resourceType: string, resourceName: string): Promise { + var azureResources: Resources = new Resources(endpoint); + var filteredResources: Array = await azureResources.getResources(resourceType, resourceName); + let resourceGroupName: string; + if(!filteredResources || filteredResources.length == 0) { + throw new Error(tl.loc('ResourceDoesntExist', resourceName)); + } + else if(filteredResources.length == 1) { + resourceGroupName = filteredResources[0].id.split("/")[4]; + } + else { + throw new Error(tl.loc('MultipleResourceGroupFoundForAppService', resourceName)); + } + + return resourceGroupName; + } +} \ No newline at end of file diff --git a/Tasks/AzureAppServiceManage/operations/ContinuousMonitoringUtils.ts b/Tasks/AzureAppServiceManage/operations/ContinuousMonitoringUtils.ts new file mode 100644 index 000000000000..e0b543501758 --- /dev/null +++ b/Tasks/AzureAppServiceManage/operations/ContinuousMonitoringUtils.ts @@ -0,0 +1,56 @@ +import tl = require('vsts-task-lib/task'); +import { AzureEndpoint } from 'azure-arm-rest/azureModels'; +import {AzureAppService } from 'azure-arm-rest/azure-arm-app-service'; +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 { AzureAppServiceUtils } from './AzureAppServiceUtils'; +import { AzureApplicationInsightsWebTestsUtils } from './AzureApplicationInsightsWebTestsUtils'; + +const APPLICATION_INSIGHTS_EXTENSION_NAME: string = "Microsoft.ApplicationInsights.AzureWebSites"; + +export async function enableContinuousMonitoring(endpoint: AzureEndpoint, appService: AzureAppService, appInsights: AzureApplicationInsights) { + try { + console.log(tl.loc('EnablingContinousMonitoring', appService.getName())); + var appDetails = await appService.get(); + var appServiceUtils = new AzureAppServiceUtils(appService); + var appInsightsResource = await appInsights.get(); + var appInsightsWebTests = new ApplicationInsightsWebTests(endpoint, appInsights.getResourceGroupName()); + var webDeployPublishingProfile = await appServiceUtils.getWebDeployPublishingProfile(); + var applicationUrl = webDeployPublishingProfile.destinationAppUrl; + if(appDetails.kind.indexOf("linux") == -1) { + var appKuduService: Kudu = await appServiceUtils.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'); + var appInsightsWebTestsUtils: AzureApplicationInsightsWebTestsUtils = new AzureApplicationInsightsWebTestsUtils(appInsightsWebTests); + await appInsightsWebTestsUtils.addWebTest(appInsightsResource, applicationUrl); + } + catch(error) { + tl.warning(error); + } + + console.log(tl.loc("ContinousMonitoringEnabled", appService.getName())); + } + catch(error) { + throw new Error(tl.loc('FailedToEnableContinuousMonitoring', error)); + } +} \ No newline at end of file diff --git a/Tasks/AzureAppServiceManage/operations/KuduServiceUtils.ts b/Tasks/AzureAppServiceManage/operations/KuduServiceUtils.ts new file mode 100644 index 000000000000..fa8e41cd57aa --- /dev/null +++ b/Tasks/AzureAppServiceManage/operations/KuduServiceUtils.ts @@ -0,0 +1,208 @@ +import tl = require('vsts-task-lib/task'); +import Q = require('q'); +import { Kudu } from 'azure-arm-rest/azure-arm-app-service-kudu'; +import webClient = require('azure-arm-rest/webClient'); + +export class KuduServiceUtils { + private _appServiceKuduService: Kudu; + + constructor(kuduService: Kudu) { + this._appServiceKuduService = kuduService; + } + + public async startContinuousWebJobs(): Promise { + console.log(tl.loc('StartingContinousWebJobs')); + var webJobs = await this._appServiceKuduService.getContinuousJobs(); + for(var webJob of webJobs) { + if(webJob.status.toLowerCase() == "running") { + console.log(tl.loc('WebJobAlreadyInRunningState', webJob.name)); + } + else { + await this._appServiceKuduService.startContinuousWebJob(webJob.name); + } + } + + console.log(tl.loc('StartedContinousWebJobs')); + } + + public async stopContinuousWebJobs(): Promise { + console.log(tl.loc('StoppingContinousWebJobs')); + var webJobs = await this._appServiceKuduService.getContinuousJobs(); + for(var webJob of webJobs) { + if(webJob.status.toLowerCase() == "stopped") { + console.log(tl.loc('WebJobAlreadyInStoppedState', webJob.name)); + } + else { + await this._appServiceKuduService.stopContinuousWebJob(webJob.name); + } + } + + console.log(tl.loc('StoppedContinousWebJobs')); + } + + public async installSiteExtensions(extensionList: Array, outputVariables?: Array): Promise { + outputVariables = outputVariables ? outputVariables : []; + var outputVariableIterator: number = 0; + var siteExtensions = await this._appServiceKuduService.getSiteExtensions(); + var anyExtensionInstalled: boolean = false; + var siteExtensionMap = {}; + for(var siteExtension of siteExtensions) { + siteExtensionMap[siteExtension.id] = siteExtension; + } + + for(var extensionID of extensionList) { + var siteExtensionDetails = null; + if(siteExtensionMap[extensionID]) { + siteExtensionDetails = siteExtensionMap[extensionID]; + console.log(tl.loc('ExtensionAlreadyInstalled', extensionID)); + } + else { + siteExtensionDetails = await this._appServiceKuduService.installSiteExtension(extensionID); + anyExtensionInstalled = true; + } + + if(outputVariableIterator < outputVariables.length) { + var extensionLocalPath: string = this._getExtensionLocalPath(siteExtensionDetails); + tl.debug('Set output Variable ' + outputVariables[outputVariableIterator] + ' to value: ' + extensionLocalPath); + tl.setVariable(outputVariables[outputVariableIterator], extensionLocalPath); + outputVariableIterator += 1; + } + } + + if(anyExtensionInstalled) { + await this.restart(); + } + } + + public async restart() { + try { + console.log(tl.loc('RestartingKuduService')); + var process0 = await this._appServiceKuduService.getProcess(0); + tl.debug(`Process 0 ID: ${process0.id}`); + await this._appServiceKuduService.killProcess(0); + await this._pollForNewProcess(0, process0.id); + console.log(tl.loc('RestartedKuduService')); + } + catch(error) { + throw Error(tl.loc('FailedToRestartKuduService', error.toString())); + } + } + + public async updateDeploymentStatus(taskResult: boolean, DeploymentID: string, customMessage: any) { + try { + var requestBody = this._getUpdateHistoryRequest(taskResult, DeploymentID, customMessage); + return await this._appServiceKuduService.updateDeployment(requestBody); + } + catch(error) { + tl.warning(error); + } + } + + private async _pollForNewProcess(processID: number, id: number) { + var retryCount = 6; + while(true) { + try { + var process = await this._appServiceKuduService.getProcess(processID); + tl.debug(`process ${processID} ID: ${process.id}`); + if(process.id != id) { + tl.debug(`New Process created`); + return process; + } + } + catch(error) { + tl.debug(`error while polling for process ${processID}: ` + error.toString()); + } + retryCount -= 1; + if(retryCount == 0) { + throw new Error(tl.loc('TimeoutWhileWaiting')); + } + + tl.debug(`sleep for 10 seconds`) + await webClient.sleepFor(10); + } + } + + private _getExtensionLocalPath(extensionInfo: JSON): string { + var extensionId: string = extensionInfo['id']; + var homeDir = "D:\\home\\"; + + if(extensionId.startsWith('python2')) { + return homeDir + "Python27"; + } + else if(extensionId.startsWith('python351') || extensionId.startsWith('python352')) { + return homeDir + "Python35"; + } + else if(extensionId.startsWith('python3')) { + return homeDir + extensionId; + } + else { + return extensionInfo['local_path']; + } + } + + private _getUpdateHistoryRequest(isDeploymentSuccess: boolean, deploymentID?: string, customMessage?: any): any { + + var status = isDeploymentSuccess ? 4 : 3; + var author = tl.getVariable('build.sourceVersionAuthor') || tl.getVariable('build.requestedfor') || + tl.getVariable('release.requestedfor') || tl.getVariable('agent.name') + + var buildUrl = tl.getVariable('build.buildUri'); + var releaseUrl = tl.getVariable('release.releaseUri'); + + var buildId = tl.getVariable('build.buildId'); + var releaseId = tl.getVariable('release.releaseId'); + + var buildNumber = tl.getVariable('build.buildNumber'); + var releaseName = tl.getVariable('release.releaseName'); + + var collectionUrl = tl.getVariable('system.TeamFoundationCollectionUri'); + var teamProject = tl.getVariable('system.teamProjectId'); + + var commitId = tl.getVariable('build.sourceVersion'); + var repoName = tl.getVariable('build.repository.name'); + var repoProvider = tl.getVariable('build.repository.provider'); + + var buildOrReleaseUrl = "" ; + deploymentID = !!deploymentID ? deploymentID : (releaseId ? releaseId : buildId) + Date.now().toString(); + + if(releaseUrl !== undefined) { + buildOrReleaseUrl = collectionUrl + teamProject + "/_apps/hub/ms.vss-releaseManagement-web.hub-explorer?releaseId=" + releaseId + "&_a=release-summary"; + } + else if(buildUrl !== undefined) { + buildOrReleaseUrl = collectionUrl + teamProject + "/_build?buildId=" + buildId + "&_a=summary"; + } + + var message = { + type : customMessage? customMessage.type : "", + commitId : commitId, + buildId : buildId, + releaseId : releaseId, + buildNumber : buildNumber, + releaseName : releaseName, + repoProvider : repoProvider, + repoName : repoName, + collectionUrl : collectionUrl, + teamProject : teamProject + }; + // Append Custom Messages to original message + for(var attribute in customMessage) { + message[attribute] = customMessage[attribute]; + } + + var deploymentLogType: string = message['type']; + var active: boolean = false; + if(deploymentLogType.toLowerCase() === "deployment" && isDeploymentSuccess) { + active = true; + } + + return { + id: deploymentID, + active : active, + status : status, + message : JSON.stringify(message), + author : author, + deployer : 'VSTS', + details : buildOrReleaseUrl + }; + } +} \ No newline at end of file diff --git a/Tasks/AzureAppServiceManage/package.json b/Tasks/AzureAppServiceManage/package.json index 97a59f40a4c5..6cfa3fe9b9e8 100644 --- a/Tasks/AzureAppServiceManage/package.json +++ b/Tasks/AzureAppServiceManage/package.json @@ -17,6 +17,7 @@ }, "homepage": "https://github.com/Microsoft/vsts-tasks#readme", "dependencies": { - "q": "1.4.1" + "q": "1.4.1", + "xml2js": "0.4.13" } } diff --git a/Tasks/AzureAppServiceManage/task.json b/Tasks/AzureAppServiceManage/task.json index e275f3b4aaef..8e573a085ace 100644 --- a/Tasks/AzureAppServiceManage/task.json +++ b/Tasks/AzureAppServiceManage/task.json @@ -380,6 +380,7 @@ "InstallingSiteExtension": "Installing site Extension '%s'", "FailedToGetResourceID": "Failed to get resource ID for resource type '%s' and resource name '%s'. Error: %s", "ContinousMonitoringEnabled": "Continuous Monitoring enabled for App Service '%s'.", + "EnablingContinousMonitoring": "Enabling continuous Monitoring for App Service '%s'.", "MultipleResourceGroupFoundForAppService": "Multiple resource group found for App Service '%s'.", "StartingContinousWebJobs": "Starting continuous WebJobs", "StartedContinousWebJobs": "Started continuous WebJobs.", diff --git a/Tasks/AzureAppServiceManage/task.loc.json b/Tasks/AzureAppServiceManage/task.loc.json index 41ddc5f87f53..e2317393ebfe 100644 --- a/Tasks/AzureAppServiceManage/task.loc.json +++ b/Tasks/AzureAppServiceManage/task.loc.json @@ -382,6 +382,7 @@ "InstallingSiteExtension": "ms-resource:loc.messages.InstallingSiteExtension", "FailedToGetResourceID": "ms-resource:loc.messages.FailedToGetResourceID", "ContinousMonitoringEnabled": "ms-resource:loc.messages.ContinousMonitoringEnabled", + "EnablingContinousMonitoring": "ms-resource:loc.messages.EnablingContinousMonitoring", "MultipleResourceGroupFoundForAppService": "ms-resource:loc.messages.MultipleResourceGroupFoundForAppService", "StartingContinousWebJobs": "ms-resource:loc.messages.StartingContinousWebJobs", "StartedContinousWebJobs": "ms-resource:loc.messages.StartedContinousWebJobs", diff --git a/Tasks/Common/azure-arm-rest/AzureServiceClient.ts b/Tasks/Common/azure-arm-rest/AzureServiceClient.ts index 550a0afa75ae..fa1984c1dff0 100644 --- a/Tasks/Common/azure-arm-rest/AzureServiceClient.ts +++ b/Tasks/Common/azure-arm-rest/AzureServiceClient.ts @@ -204,13 +204,7 @@ export class ServiceClient { } } - private sleepFor(sleepDurationInSeconds): Promise { - return new Promise((resolve, reeject) => { - setTimeout(resolve, sleepDurationInSeconds * 1000); - }); - } - - public getFormattedError(error: any) { + public getFormattedError(error: any): string { if(error && error.message) { if(error.statusCode) { var errorMessage = typeof error.message.valueOf() == 'string' ? error.message @@ -223,4 +217,10 @@ export class ServiceClient { return error; } + + private sleepFor(sleepDurationInSeconds): Promise { + return new Promise((resolve, reeject) => { + setTimeout(resolve, sleepDurationInSeconds * 1000); + }); + } } diff --git a/Tasks/Common/azure-arm-rest/Tests/L0-azure-arm-app-service.ts b/Tasks/Common/azure-arm-rest/Tests/L0-azure-arm-app-service.ts index b1a529a34ebc..2de56a487226 100644 --- a/Tasks/Common/azure-arm-rest/Tests/L0-azure-arm-app-service.ts +++ b/Tasks/Common/azure-arm-rest/Tests/L0-azure-arm-app-service.ts @@ -21,12 +21,12 @@ export function AzureAppServiceMockTests() { swap(tr); console.log("\tvalidating get"); get(tr); - console.log("\tvalidating monitorAppState"); - monitorAppState(tr); + //console.log("\tvalidating monitorAppState"); + //monitorAppState(tr); console.log("\tvalidating getPublishingProfileWithSecrets"); getPublishingProfileWithSecrets(tr); - console.log("\tvalidating getWebDeployPublishingProfile"); - getWebDeployPublishingProfile(tr); + //console.log("\tvalidating getWebDeployPublishingProfile"); + //getWebDeployPublishingProfile(tr); console.log("\tvalidating getApplicationSettings"); getApplicationSettings(tr); console.log("\tvalidating updateApplicationSettings"); @@ -35,8 +35,8 @@ export function AzureAppServiceMockTests() { getConfiguration(tr); console.log("\tvalidating updateConfiguration"); updateConfiguration(tr); - console.log("\tvalidating getKuduService"); - getKuduService(tr); + //console.log("\tvalidating getKuduService"); + //getKuduService(tr); } catch(error) { passed = false; diff --git a/Tasks/Common/azure-arm-rest/Tests/L0-azure-arm-appinsights-webtests-tests.ts b/Tasks/Common/azure-arm-rest/Tests/L0-azure-arm-appinsights-webtests-tests.ts index 4534513b02c1..42dc26aec381 100644 --- a/Tasks/Common/azure-arm-rest/Tests/L0-azure-arm-appinsights-webtests-tests.ts +++ b/Tasks/Common/azure-arm-rest/Tests/L0-azure-arm-appinsights-webtests-tests.ts @@ -15,8 +15,6 @@ export function ApplicationInsightsTests() { list(tr); console.log("\tvalidating create"); create(tr); - console.log("\tvalidating addWebTest"); - addWebTest(tr); } catch(error) { passed = false; @@ -39,8 +37,4 @@ function list(tr) { function create(tr) { assert(tr.stdOutContained('FailedToCreateWebTests'), 'Should have printed: FailedToCreateWebTests'); assert(tr.stdOutContained('added web test MOCK_TEST_1.'), 'added web test MOCK_TEST_1.'); -} - -function addWebTest(tr) { - assert(tr.stdOutContained('WebTestAlreadyConfigured http://MOCK_APP_1.azurewebsites.net'), 'Should have printed: WebTestAlreadyConfigured http://MOCK_APP_1.azurewebsites.net'); } \ No newline at end of file diff --git a/Tasks/Common/azure-arm-rest/Tests/azure-arm-app-service-kudu-tests.ts b/Tasks/Common/azure-arm-rest/Tests/azure-arm-app-service-kudu-tests.ts index ef17d018302a..bbeb74a7ef49 100644 --- a/Tasks/Common/azure-arm-rest/Tests/azure-arm-app-service-kudu-tests.ts +++ b/Tasks/Common/azure-arm-rest/Tests/azure-arm-app-service-kudu-tests.ts @@ -6,11 +6,12 @@ import { mockKuduServiceTests } from './mock_utils'; mockKuduServiceTests(); export class KuduTests { + public static async updateDeployment() { try { var kudu = new Kudu('http://MOCK_SCM_WEBSITE', 'MOCK_SCM_USERNAME', 'MOCK_SCM_PASSWORD'); - await kudu.updateDeployment(true, 'MOCK_DEPLOYMENT_ID', {type: 'Deployment'}); + await kudu.updateDeployment({id: 'MOCK_DEPLOYMENT_ID', type: 'Deployment'}); } catch(error) { tl.error(error); @@ -19,7 +20,7 @@ export class KuduTests { try { var kudu = new Kudu('http://FAIL_MOCK_SCM_WEBSITE', 'MOCK_SCM_USERNAME', 'MOCK_SCM_PASSWORD'); - await kudu.updateDeployment(true, 'MOCK_DEPLOYMENT_ID', {type: 'Deployment'}); + await kudu.updateDeployment({id: 'MOCK_DEPLOYMENT_ID', type: 'Deployment'}); tl.setResult(tl.TaskResult.Failed, 'KuduTests.updateDeployment() should have failed but passed'); } catch(error) { @@ -27,6 +28,7 @@ export class KuduTests { } } + public static async getContinuousJobs() { try { var kudu = new Kudu('http://MOCK_SCM_WEBSITE', 'MOCK_SCM_USERNAME', 'MOCK_SCM_PASSWORD'); diff --git a/Tasks/Common/azure-arm-rest/Tests/azure-arm-app-service-tests.ts b/Tasks/Common/azure-arm-rest/Tests/azure-arm-app-service-tests.ts index 528ec3516754..36584dd5ecaf 100644 --- a/Tasks/Common/azure-arm-rest/Tests/azure-arm-app-service-tests.ts +++ b/Tasks/Common/azure-arm-rest/Tests/azure-arm-app-service-tests.ts @@ -95,6 +95,7 @@ class AzureAppServiceTests { } + /* public static async monitorAppState() { var appSerivce: AzureAppService = new AzureAppService(endpoint, "MOCK_RESOURCE_GROUP_NAME", "MOCK_APP_SERVICE_NAME"); appSerivce.monitorAppState("Running") @@ -103,7 +104,7 @@ class AzureAppServiceTests { tl.setResult(tl.TaskResult.Failed, 'AzureAppServiceTests.monitorAppState() should have passed but failed'); }); } - + */ public static async getPublishingProfileWithSecrets() { var appSerivce: AzureAppService = new AzureAppService(endpoint, "MOCK_RESOURCE_GROUP_NAME", "MOCK_APP_SERVICE_NAME"); appSerivce.getPublishingProfileWithSecrets().then((value) => { @@ -121,6 +122,7 @@ class AzureAppServiceTests { }); } + /* public static async getWebDeployPublishingProfile() { var appSerivce: AzureAppService = new AzureAppService(endpoint, "MOCK_RESOURCE_GROUP_NAME", "MOCK_APP_SERVICE_NAME"); appSerivce.getWebDeployPublishingProfile().then((value) => { @@ -131,6 +133,7 @@ class AzureAppServiceTests { }); } + */ public static async getPublishingCredentials() { var appSerivce: AzureAppService = new AzureAppService(endpoint, "MOCK_RESOURCE_GROUP_NAME", "MOCK_APP_SERVICE_NAME"); appSerivce.getPublishingCredentials().then((value) => { @@ -239,6 +242,7 @@ class AzureAppServiceTests { }); } + /* public static async getKuduService() { var appSerivce: AzureAppService = new AzureAppService(endpoint, "MOCK_RESOURCE_GROUP_NAME", "MOCK_APP_SERVICE_NAME"); appSerivce.getKuduService().then((value) => { @@ -248,6 +252,7 @@ class AzureAppServiceTests { tl.setResult(tl.TaskResult.Failed, 'AzureAppServiceTests.updateConfiguration() should have passed but failed'); }); } + */ } AzureAppServiceTests.start(); @@ -255,12 +260,12 @@ AzureAppServiceTests.stop(); AzureAppServiceTests.restart(); AzureAppServiceTests.swap(); AzureAppServiceTests.get(); -AzureAppServiceTests.monitorAppState(); +// AzureAppServiceTests.monitorAppState(); AzureAppServiceTests.getPublishingProfileWithSecrets(); -AzureAppServiceTests.getWebDeployPublishingProfile(); +// AzureAppServiceTests.getWebDeployPublishingProfile(); AzureAppServiceTests.getPublishingCredentials(); AzureAppServiceTests.getApplicationSettings(); AzureAppServiceTests.updateApplicationSettings(); AzureAppServiceTests.getConfiguration(); AzureAppServiceTests.updateConfiguration(); -AzureAppServiceTests.getKuduService(); +// AzureAppServiceTests.getKuduService(); diff --git a/Tasks/Common/azure-arm-rest/Tests/azure-arm-appinsights-webtests-tests.ts b/Tasks/Common/azure-arm-rest/Tests/azure-arm-appinsights-webtests-tests.ts index 2fd000f46376..c0bed0e7495f 100644 --- a/Tasks/Common/azure-arm-rest/Tests/azure-arm-appinsights-webtests-tests.ts +++ b/Tasks/Common/azure-arm-rest/Tests/azure-arm-appinsights-webtests-tests.ts @@ -9,7 +9,7 @@ var endpoint = getMockEndpoint(); mockAzureARMAppInsightsWebTests(); export class ApplicationInsightsWebTestsTests { - public static async get() { + public static async list() { let appInsightsWebTests: ApplicationInsightsWebTests = new ApplicationInsightsWebTests(endpoint, "MOCK_RESOURCE_GROUP_NAME"); try { console.log(`WEB TEST COUNT: ${(await appInsightsWebTests.list()).length}`); @@ -20,24 +20,17 @@ export class ApplicationInsightsWebTestsTests { } } - public static async addWebTest() { - let appInsightsWebTests: ApplicationInsightsWebTests = new ApplicationInsightsWebTests(endpoint, "MOCK_RESOURCE_GROUP_NAME"); - try { - await appInsightsWebTests.addWebTest({id: "hidden-link:/subscriptions/MOCK_SUBSCRIPTION_ID/resourceGroups/MOCK_RESOURCE_GROUP_NAME/providers/microsoft.insights/components/MOCK_APP_INSIGHTS_1".toLowerCase()}, "http://MOCK_APP_1.azurewebsites.net"); - } - catch(error) { - console.log(error); - tl.setResult(tl.TaskResult.Failed, 'ApplicationInsightsWebTestsTests.addWebTest() should have passed but failed'); - } - } - public static async create() { let appInsightsWebTests: ApplicationInsightsWebTests = new ApplicationInsightsWebTests(endpoint, "MOCK_RESOURCE_GROUP_NAME"); try { await appInsightsWebTests.create({ + type: '', + location: '', + tags: {}, + name: 'VSTS_MOCK_TEST', id: "hidden-link:/subscriptions/MOCK_SUBSCRIPTION_ID/resourceGroups/MOCK_RESOURCE_GROUP_NAME/providers/microsoft.insights/components/MOCK_APP_INSIGHTS_1".toLowerCase() - }, "http://MOCK_APP_NEW.azurewebsites.net", "VSTS_MOCK_TEST"); + }); } catch(error) { console.log(error); @@ -46,8 +39,12 @@ export class ApplicationInsightsWebTestsTests { try { await appInsightsWebTests.create({ + type: '', + location: '', + tags: {}, + name: 'VSTS_MOCK_TEST_FAIL', id: "hidden-link:/subscriptions/MOCK_SUBSCRIPTION_ID/resourceGroups/MOCK_RESOURCE_GROUP_NAME/providers/microsoft.insights/components/MOCK_APP_INSIGHTS_FAIL".toLowerCase() - }, "http://MOCK_APP_NEW.azurewebsites.net", "VSTS_MOCK_TEST_FAIL"); + }); tl.setResult(tl.TaskResult.Failed, 'ApplicationInsightsWebTestsTests.create() should have failed but passed'); } @@ -57,6 +54,5 @@ export class ApplicationInsightsWebTestsTests { } } -ApplicationInsightsWebTestsTests.get(); -ApplicationInsightsWebTestsTests.addWebTest(); +ApplicationInsightsWebTestsTests.list(); ApplicationInsightsWebTestsTests.create(); \ No newline at end of file diff --git a/Tasks/Common/azure-arm-rest/Tests/mock_utils.ts b/Tasks/Common/azure-arm-rest/Tests/mock_utils.ts index 4027b7bff872..a5a77e95ae92 100644 --- a/Tasks/Common/azure-arm-rest/Tests/mock_utils.ts +++ b/Tasks/Common/azure-arm-rest/Tests/mock_utils.ts @@ -1,6 +1,7 @@ import { AzureEndpoint, WebTest } from '../azureModels'; import { ApplicationInsightsWebTests } from '../azure-arm-appinsights-webtests'; import * as querystring from "querystring"; +import { ApplicationTokenCredentials } from '../azure-arm-common'; export var nock = require('nock'); export function getMockEndpoint() { @@ -15,7 +16,9 @@ export function getMockEndpoint() { tenantID: "MOCK_TENANT_ID", url: "https://management.azure.com/", environmentAuthorityUrl: "https://login.windows.net/", - activeDirectoryResourceID: "https://management.azure.com/" + activeDirectoryResourceID: "https://management.azure.com/", + applicationTokenCredentials: new ApplicationTokenCredentials("MOCK_SPN_ID", "MOCK_TENANT_ID", "MOCK_SPN_KEY", "https://management.azure.com/", + "https://login.windows.net/", "https://management.azure.com/", false) } nock("https://login.windows.net", { @@ -37,9 +40,23 @@ export function getMockEndpoint() { } export function mockAzureARMAppInsightsWebTests() { - var MockWebTest1: WebTest = (new ApplicationInsightsWebTests(getMockEndpoint(), "MOCK_RESOURCE_GROUP_NAME")).configureNewWebTest("MOCK_APP_INSIGHTS_1", "http://MOCK_APP_1.azurewebsites.net", "MOCK_TEST_1"); + var MockWebTest1 = { + type: '', + location: '', + tags: {}, + name: 'MOCK_TEST_1', + id: "hidden-link:/subscriptions/MOCK_SUBSCRIPTION_ID/resourceGroups/MOCK_RESOURCE_GROUP_NAME/providers/microsoft.insights/components/MOCK_APP_INSIGHTS_1".toLowerCase() + }; + + var MockWebTest2 = { + type: '', + location: '', + tags: {}, + name: 'MOCK_TEST_2', + id: "hidden-link:/subscriptions/MOCK_SUBSCRIPTION_ID/resourceGroups/MOCK_RESOURCE_GROUP_NAME/providers/microsoft.insights/components/MOCK_APP_INSIGHTS_1".toLowerCase() + }; + MockWebTest1.tags = {"hidden-link:/subscriptions/MOCK_SUBSCRIPTION_ID/resourceGroups/MOCK_RESOURCE_GROUP_NAME/providers/microsoft.insights/components/MOCK_APP_INSIGHTS_1": "Resource"}; - var MockWebTest2: WebTest = (new ApplicationInsightsWebTests(getMockEndpoint(), "MOCK_RESOURCE_GROUP_NAME")).configureNewWebTest("MOCK_APP_INSIGHTS_2", "http://MOCK_APP_2.azurewebsites.net", "MOCK_TEST_2"); MockWebTest2.tags = {"hidden-link:/subscriptions/MOCK_SUBSCRIPTION_ID/resourceGroups/MOCK_RESOURCE_GROUP_NAME/providers/microsoft.insights/components/MOCK_APP_INSIGHTS_1": "Resource"}; nock('https://management.azure.com', { @@ -59,7 +76,6 @@ export function mockAzureARMAppInsightsWebTests() { "content-type": "application/json; charset=utf-8" }).put("/subscriptions/MOCK_SUBSCRIPTION_ID/resourceGroups/MOCK_RESOURCE_GROUP_NAME/providers/microsoft.insights/webtests/VSTS_MOCK_TEST_FAIL?api-version=2015-05-01") .reply(501, 'Failed to add new web test').persist(); - } export function mockAzureApplicationInsightsTests() { diff --git a/Tasks/Common/azure-arm-rest/azure-arm-app-service-kudu.ts b/Tasks/Common/azure-arm-rest/azure-arm-app-service-kudu.ts index 0487ad3cc1e8..833b44c95fef 100644 --- a/Tasks/Common/azure-arm-rest/azure-arm-app-service-kudu.ts +++ b/Tasks/Common/azure-arm-rest/azure-arm-app-service-kudu.ts @@ -47,12 +47,11 @@ export class Kudu { this._client = new KuduServiceManagementClient(scmUri, base64EncodedCredential); } - public async updateDeployment(isSucceeded: boolean, deploymentID?: string, customMessage?: any) { - var deploymentStatusBody = this._getUpdateHistoryRequest(isSucceeded, deploymentID, customMessage); + public async updateDeployment(requestBody: any): Promise { var httpRequest = new webClient.WebRequest(); httpRequest.method = 'PUT'; - httpRequest.body = JSON.stringify(deploymentStatusBody); - httpRequest.uri = this._client.getRequestUri(`/api/deployments/${deploymentStatusBody.id}`); + httpRequest.body = JSON.stringify(requestBody); + httpRequest.uri = this._client.getRequestUri(`/api/deployments/${requestBody.id}`); try { var response = await this._client.beginRequest(httpRequest); @@ -209,77 +208,6 @@ export class Kudu { } } - public async installSiteExtensions(extensionList: Array, outputVariables?: Array) { - outputVariables = outputVariables ? outputVariables : []; - var outputVariableIterator: number = 0; - var siteExtensions = await this.getSiteExtensions(); - var anyExtensionInstalled: boolean = false; - var siteExtensionMap = {}; - for(var siteExtension of siteExtensions) { - siteExtensionMap[siteExtension.id] = siteExtension; - } - - for(var extensionID of extensionList) { - var siteExtensionDetails = null; - if(siteExtensionMap[extensionID]) { - siteExtensionDetails = siteExtensionMap[extensionID]; - console.log(tl.loc('ExtensionAlreadyInstalled', extensionID)); - } - else { - siteExtensionDetails = await this.installSiteExtension(extensionID); - anyExtensionInstalled = true; - } - - if(outputVariableIterator < outputVariables.length) { - var extensionLocalPath: string = this._getExtensionLocalPath(siteExtensionDetails); - tl.debug('Set output Variable ' + outputVariables[outputVariableIterator] + ' to value: ' + extensionLocalPath); - tl.setVariable(outputVariables[outputVariableIterator], this._getExtensionLocalPath(siteExtensionDetails)); - outputVariableIterator += 1; - } - } - - if(anyExtensionInstalled) { - await this.restart(); - } - } - - public async restart() { - try { - console.log(tl.loc('RestartingKuduService')); - var process0 = await this.getProcess(0); - tl.debug(`Process 0 ID: ${process0.id}`); - await this.killProcess(0); - await this._pollForNewProcess(0, process0.id); - console.log(tl.loc('RestartedKuduService')); - } - catch(error) { - throw Error(tl.loc('FailedToRestartKuduService', error.toString())); - } - } - public async startContinuousWebJobs() { - var webJobs = await this.getContinuousJobs(); - for(var webJob of webJobs) { - if(webJob.status.toLowerCase() == "running") { - console.log(tl.loc('WebJobAlreadyInRunningState', webJob.name)); - } - else { - await this.startContinuousWebJob(webJob.name); - } - } - } - - public async stopContinuousWebJobs() { - var webJobs = await this.getContinuousJobs(); - for(var webJob of webJobs) { - if(webJob.status.toLowerCase() == "stopped") { - console.log(tl.loc('WebJobAlreadyInStoppedState', webJob.name)); - } - else { - await this.stopContinuousWebJob(webJob.name); - } - } - } - private _getFormattedError(error: any) { if(error && error.statusCode) { return `${error.statusMessage} (CODE: ${error.statusCode})`; @@ -294,112 +222,4 @@ export class Kudu { return error; } - - private async _pollForNewProcess(processID: number, id: number) { - var retryCount = 6; - while(true) { - try { - var process = await this.getProcess(processID); - tl.debug(`process ${processID} ID: ${process.id}`); - if(process.id != id) { - tl.debug(`New Process created`); - return process; - } - } - catch(error) { - tl.debug(`error while polling for process ${processID}: ` + error.toString()); - } - retryCount -= 1; - if(retryCount == 0) { - throw new Error(tl.loc('TimeoutWhileWaiting')); - } - - tl.debug(`sleep for 10 seconds`) - await webClient.sleepFor(10); - } - } - - private _getUpdateHistoryRequest(isDeploymentSuccess: boolean, deploymentID?: string, customMessage?: any): any { - - var status = isDeploymentSuccess ? 4 : 3; - var author = tl.getVariable('build.sourceVersionAuthor') || tl.getVariable('build.requestedfor') || - tl.getVariable('release.requestedfor') || tl.getVariable('agent.name') - - var buildUrl = tl.getVariable('build.buildUri'); - var releaseUrl = tl.getVariable('release.releaseUri'); - - var buildId = tl.getVariable('build.buildId'); - var releaseId = tl.getVariable('release.releaseId'); - - var buildNumber = tl.getVariable('build.buildNumber'); - var releaseName = tl.getVariable('release.releaseName'); - - var collectionUrl = tl.getVariable('system.TeamFoundationCollectionUri'); - var teamProject = tl.getVariable('system.teamProjectId'); - - var commitId = tl.getVariable('build.sourceVersion'); - var repoName = tl.getVariable('build.repository.name'); - var repoProvider = tl.getVariable('build.repository.provider'); - - var buildOrReleaseUrl = "" ; - deploymentID = !!deploymentID ? deploymentID : (releaseId ? releaseId : buildId) + Date.now().toString(); - - if(releaseUrl !== undefined) { - buildOrReleaseUrl = collectionUrl + teamProject + "/_apps/hub/ms.vss-releaseManagement-web.hub-explorer?releaseId=" + releaseId + "&_a=release-summary"; - } - else if(buildUrl !== undefined) { - buildOrReleaseUrl = collectionUrl + teamProject + "/_build?buildId=" + buildId + "&_a=summary"; - } - - var message = { - type : customMessage? customMessage.type : "", - commitId : commitId, - buildId : buildId, - releaseId : releaseId, - buildNumber : buildNumber, - releaseName : releaseName, - repoProvider : repoProvider, - repoName : repoName, - collectionUrl : collectionUrl, - teamProject : teamProject - }; - // Append Custom Messages to original message - for(var attribute in customMessage) { - message[attribute] = customMessage[attribute]; - } - - var deploymentLogType: string = message['type']; - var active: boolean = false; - if(deploymentLogType.toLowerCase() === "deployment" && isDeploymentSuccess) { - active = true; - } - - return { - id: deploymentID, - active : active, - status : status, - message : JSON.stringify(message), - author : author, - deployer : 'VSTS', - details : buildOrReleaseUrl - }; - } - - private _getExtensionLocalPath(extensionInfo: JSON): string { - var extensionId: string = extensionInfo['id']; - var homeDir = "D:\\home\\"; - - if(extensionId.startsWith('python2')) { - return homeDir + "Python27"; - } - else if(extensionId.startsWith('python351') || extensionId.startsWith('python352')) { - return homeDir + "Python35"; - } - else if(extensionId.startsWith('python3')) { - return homeDir + extensionId; - } - else { - return extensionInfo['local_path']; - } - } } \ No newline at end of file diff --git a/Tasks/Common/azure-arm-rest/azure-arm-app-service.ts b/Tasks/Common/azure-arm-rest/azure-arm-app-service.ts index 44649a843f14..ec197cf7758a 100644 --- a/Tasks/Common/azure-arm-rest/azure-arm-app-service.ts +++ b/Tasks/Common/azure-arm-rest/azure-arm-app-service.ts @@ -3,7 +3,7 @@ import tl = require('vsts-task-lib/task'); import util = require('util'); import webClient = require('./webClient'); import Q = require('q'); -import { +import { AzureEndpoint, AzureAppServiceConfigurationDetails } from './azureModels'; @@ -13,11 +13,10 @@ import { ToError } from './AzureServiceClient'; import { Kudu } from './azure-arm-app-service-kudu'; - +import constants = require('./constants'); var parseString = require('xml2js').parseString; export class AzureAppService { - private _endpoint: AzureEndpoint; private _resourceGroup: string; private _name: string; private _slot: string; @@ -27,17 +26,14 @@ export class AzureAppService { private _appServicePublishingProfile: any; constructor(endpoint: AzureEndpoint, resourceGroup: string, name: string, slot?: string, appKind?: string) { - var credentials = new msRestAzure.ApplicationTokenCredentials(endpoint.servicePrincipalClientID, endpoint.tenantID, endpoint.servicePrincipalKey, - endpoint.url, endpoint.environmentAuthorityUrl, endpoint.activeDirectoryResourceID, endpoint.environment.toLowerCase() == 'azurestack'); - this._client = new ServiceClient(credentials, endpoint.subscriptionID, 30); - this._endpoint = endpoint; + this._client = new ServiceClient(endpoint.applicationTokenCredentials, endpoint.subscriptionID, 30); this._resourceGroup = resourceGroup; this._name = name; - this._slot = (slot && slot.toLowerCase() == 'production') ? null : slot; + this._slot = (slot && slot.toLowerCase() == constants.productionSlot) ? null : slot; this._appKind = appKind; } - public async start() { + public async start(): Promise { try { var webRequest = new webClient.WebRequest(); webRequest.method = 'POST'; @@ -54,14 +50,13 @@ export class AzureAppService { } console.log(tl.loc('StartedAppService', this._getFormattedName())); - return response.statusCode; } catch(error) { throw Error(tl.loc('FailedToStartAppService', this._getFormattedName(), this._client.getFormattedError(error))); } } - public async stop() { + public async stop(): Promise { try { var webRequest = new webClient.WebRequest(); webRequest.method = 'POST'; @@ -78,14 +73,13 @@ export class AzureAppService { } console.log(tl.loc('StoppedAppService', this._getFormattedName())); - return response.statusCode; } catch(error) { throw Error(tl.loc('FailedToStopAppService', this._getFormattedName(), this._client.getFormattedError(error))); } } - public async restart() { + public async restart(): Promise { try { var webRequest = new webClient.WebRequest(); webRequest.method = 'POST'; @@ -102,14 +96,13 @@ export class AzureAppService { } console.log(tl.loc('RestartedAppService', this._getFormattedName())); - return response.body; } catch(error) { throw Error(tl.loc('FailedToRestartAppService', this._getFormattedName(), this._client.getFormattedError(error))); } } - public async swap(slotName: string, preserveVNet?: boolean) { + public async swap(slotName: string, preserveVNet?: boolean): Promise { try { var webRequest = new webClient.WebRequest(); webRequest.method = 'POST'; @@ -136,7 +129,6 @@ export class AzureAppService { } console.log(tl.loc('SwappedAppServiceSlotSlots', this._name, this.getSlot(), slotName)); - return response.statusCode; } catch(error) { throw Error(tl.loc('FailedToSwapAppServiceSlotSlots', this._name, this.getSlot(), slotName, this._client.getFormattedError(error))); @@ -151,30 +143,6 @@ export class AzureAppService { return this._appServiceConfigurationDetails; } - public async monitorAppState(state: string) { - state = state.toLowerCase(); - if(["running", "stopped"].indexOf(state) == -1) { - throw new Error(tl.loc('InvalidMonitorAppState', state)); - } - - while(true) { - var appDetails = await this.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 getPublishingProfileWithSecrets(force?: boolean) { if(force || !this._appServicePublishingProfile) { this._appServicePublishingProfile = await this._getPublishingProfileWithSecrets(); @@ -183,57 +151,6 @@ export class AzureAppService { return this._appServicePublishingProfile; } - public async getWebDeployPublishingProfile() { - var publishingProfile = await this.getPublishingProfileWithSecrets(); - var defer = Q.defer(); - 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(numberOfTimes: number) { - numberOfTimes = numberOfTimes ? numberOfTimes : 1; - try { - var applicationUrl: string = (await this.getWebDeployPublishingProfile()).destinationAppUrl; - } - catch(error) { - tl.debug(`Unable to get publishing profile for ping application. Error: ${this._client.getFormattedError(error)}`); - } - - if(!applicationUrl) { - tl.debug('Application Url not found.'); - return; - } - - tl.debug(`Ping App Service for '${numberOfTimes}' time(s).`); - var webRequest = new webClient.WebRequest(); - webRequest.method = 'GET'; - webRequest.uri = applicationUrl; - - while(numberOfTimes > 0) { - try { - 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: ${this._client.getFormattedError(error)}`); - } - finally { - numberOfTimes -= 1; - } - } - } - public async getPublishingCredentials() { try { var httpRequest = new webClient.WebRequest(); @@ -257,7 +174,7 @@ export class AzureAppService { } } - public async getApplicationSettings() { + public async getApplicationSettings(): Promise { try { var httpRequest = new webClient.WebRequest(); httpRequest.method = 'POST'; @@ -280,7 +197,7 @@ export class AzureAppService { } } - public async updateApplicationSettings(applicationSettings) { + public async updateApplicationSettings(applicationSettings): Promise { try { var httpRequest = new webClient.WebRequest(); httpRequest.method = 'PUT'; @@ -304,7 +221,7 @@ export class AzureAppService { } } - public async patchApplicationSettings(properties) { + public async patchApplicationSettings(properties): Promise { var applicationSettings = await this.getApplicationSettings(); for(var key in properties) { applicationSettings.properties[key] = properties[key]; @@ -314,7 +231,7 @@ export class AzureAppService { } - public async getConfiguration() { + public async getConfiguration(): Promise { try { var httpRequest = new webClient.WebRequest(); httpRequest.method = 'GET'; @@ -337,7 +254,7 @@ export class AzureAppService { } } - public async updateConfiguration(applicationSettings) { + public async updateConfiguration(applicationSettings): Promise { try { var httpRequest = new webClient.WebRequest(); httpRequest.method = 'PUT'; @@ -361,7 +278,7 @@ export class AzureAppService { } } - public async patchConfiguration(properties) { + public async patchConfiguration(properties): Promise { var applicationSettings = await this.getConfiguration(); for(var key in properties) { applicationSettings.properties[key] = properties[key]; @@ -374,15 +291,6 @@ export class AzureAppService { public getSlot(): string { return this._slot ? this._slot : "production"; } - - public async getKuduService() { - var publishingCredentials = await this.getPublishingCredentials(); - if(publishingCredentials.properties["scmUri"]) { - tl.setVariable(`AZURE_APP_SERVICE_KUDU_${this._name}_${this.getSlot()}_PASSWORD`, publishingCredentials.properties["publishingPassword"], true); - return new Kudu(publishingCredentials.properties["scmUri"], publishingCredentials.properties["publishingUserName"], publishingCredentials.properties["publishingPassword"]); - } - throw Error(tl.loc('KuduSCMDetailsAreEmpty')); - } private async _getPublishingProfileWithSecrets() { try { @@ -435,4 +343,8 @@ export class AzureAppService { private _getFormattedName(): string { return this._slot ? `${this._name}-${this._slot}` : this._name; } -} \ No newline at end of file + + public getName(): string { + return this._name; + } + } \ No newline at end of file diff --git a/Tasks/Common/azure-arm-rest/azure-arm-appinsights-webtests.ts b/Tasks/Common/azure-arm-rest/azure-arm-appinsights-webtests.ts index 193b4ffb3c0e..38fbb96e234f 100644 --- a/Tasks/Common/azure-arm-rest/azure-arm-appinsights-webtests.ts +++ b/Tasks/Common/azure-arm-rest/azure-arm-appinsights-webtests.ts @@ -10,14 +10,9 @@ import { AzureEndpoint, WebTest } from './azureModels'; export class ApplicationInsightsWebTests { private _resourceGroupName: string; private _client: ServiceClient; - private _endpoint: AzureEndpoint; - constructor(endpoint: AzureEndpoint, resourceGroup?: string) { - let credentials = new msRestAzure.ApplicationTokenCredentials(endpoint.servicePrincipalClientID, endpoint.tenantID, endpoint.servicePrincipalKey, - endpoint.url, endpoint.environmentAuthorityUrl, endpoint.activeDirectoryResourceID, endpoint.environment.toLowerCase() == 'azurestack'); - - this._endpoint = endpoint; - this._client = new ServiceClient(credentials, endpoint.subscriptionID, 30); + constructor(endpoint: AzureEndpoint, resourceGroup: string) { + this._client = new ServiceClient(endpoint.applicationTokenCredentials, endpoint.subscriptionID, 30); this._resourceGroupName = resourceGroup; } @@ -54,10 +49,9 @@ export class ApplicationInsightsWebTests { } } - public async create(appInsightsResource: any, applicationUrl: string, testName?: string): Promise { + public async create(webTestData: WebTest): Promise { let httpRequest = new webClient.WebRequest(); - let webTestData: WebTest = this.configureNewWebTest(appInsightsResource, applicationUrl, testName); httpRequest.method = 'PUT'; httpRequest.body = JSON.stringify(webTestData); @@ -74,84 +68,11 @@ export class ApplicationInsightsWebTests { } tl.debug(`added web test ${response.body.name}.`); - return response.body; + return response.body as WebTest; } catch(error) { throw Error(tl.loc("FailedToCreateWebTests", this._client.getFormattedError(error))); } } - - public async addWebTest(appInsightsResource: any, applicationUrl: string, webTestName?: string) { - let webTests = await this.list(); - for(let webTest of webTests) { - let isTagPresent: boolean = false; - let isApplicationUrlPresent: boolean = false; - for(let tag in webTest.tags) { - if(tag.toLowerCase().indexOf(appInsightsResource.id.toLowerCase()) != -1) { - isTagPresent = true; - break; - } - } - - isApplicationUrlPresent = webTest.properties['Configuration'].WebTest.toLowerCase().indexOf(applicationUrl.toLowerCase()) != -1; - if(isTagPresent && isApplicationUrlPresent) { - console.log(tl.loc('WebTestAlreadyConfigured', applicationUrl)); - return; - } - } - - await this.create(appInsightsResource, applicationUrl, webTestName); - } - - public configureNewWebTest(appInsightsResource: any, applicationUrl: string, testName?: string): WebTest { - let webTestName = testName ? testName: "vsts-web-test-" + Date.now(); - let webTestData = JSON.parse(JSON.stringify(this._webTestData)); - webTestData.name = webTestName; - webTestData.properties.Name = webTestName; - webTestData.properties.SyntheticMonitorId = webTestName; - webTestData.location = appInsightsResource.location; - webTestData.tags["hidden-link:" + appInsightsResource.id] = "Resource"; - webTestData.properties.Configuration.WebTest = webTestData.properties.Configuration.WebTest.replace("{WEB_TEST_NAME}", webTestName); - webTestData.properties.Configuration.WebTest = webTestData.properties.Configuration.WebTest.replace("{APPLICATION_URL}", applicationUrl); - - return webTestData; - } - - private _webTestData: WebTest = { - "name": "", - "location": "", - "tags": {}, - "type": "microsoft.insights/webtests", - "properties": { - "SyntheticMonitorId": "", - "Name": "", - "Description": "", - "Enabled": true, - "Frequency": 300, - "Timeout": 120, - "Kind": "ping", - "RetryEnabled": true, - "Locations": [ - { - "Id": "us-tx-sn1-azr" - }, - { - "Id": "us-il-ch1-azr" - }, - { - "Id": "us-ca-sjc-azr" - }, - { - "Id": "us-va-ash-azr" - }, - { - "Id": "us-fl-mia-edge" - } - ], - "Configuration": { - "WebTest": " " - } - } - } } \ No newline at end of file diff --git a/Tasks/Common/azure-arm-rest/azure-arm-appinsights.ts b/Tasks/Common/azure-arm-rest/azure-arm-appinsights.ts index dd76b2f65a95..850deafba1c6 100644 --- a/Tasks/Common/azure-arm-rest/azure-arm-appinsights.ts +++ b/Tasks/Common/azure-arm-rest/azure-arm-appinsights.ts @@ -14,10 +14,7 @@ export class AzureApplicationInsights { private _client: ServiceClient; constructor(endpoint: AzureEndpoint, resourceGroupName: string, name: string) { - var credentials = new msRestAzure.ApplicationTokenCredentials(endpoint.servicePrincipalClientID, endpoint.tenantID, endpoint.servicePrincipalKey, - endpoint.url, endpoint.environmentAuthorityUrl, endpoint.activeDirectoryResourceID, endpoint.environment.toLowerCase() == 'azurestack'); - - this._client = new ServiceClient(credentials, endpoint.subscriptionID, 30); + this._client = new ServiceClient(endpoint.applicationTokenCredentials, endpoint.subscriptionID, 30); this._endpoint = endpoint; this._resourceGroupName = resourceGroupName; this._name = name; @@ -46,7 +43,7 @@ export class AzureApplicationInsights { } } - public async update(insightProperties: any) { + public async update(insightProperties: any): Promise { var httpRequest = new webClient.WebRequest(); httpRequest.method = 'PUT'; httpRequest.body = JSON.stringify(insightProperties); @@ -69,10 +66,6 @@ export class AzureApplicationInsights { } } - public getEndpoint(): AzureEndpoint { - return this._endpoint; - } - public getResourceGroupName(): string { return this._resourceGroupName; } diff --git a/Tasks/Common/azure-arm-rest/azure-arm-endpoint.ts b/Tasks/Common/azure-arm-rest/azure-arm-endpoint.ts index a7e8e2030338..92fa9423f56e 100644 --- a/Tasks/Common/azure-arm-rest/azure-arm-endpoint.ts +++ b/Tasks/Common/azure-arm-rest/azure-arm-endpoint.ts @@ -2,10 +2,14 @@ import tl = require('vsts-task-lib/task'); import Q = require('q'); import webClient = require("./webClient"); import { AzureEndpoint } from "./azureModels"; +import { ApplicationTokenCredentials } from './azure-arm-common'; +import constants = require('./constants'); export class AzureRMEndpoint { public endpoint: AzureEndpoint; private _connectedServiceName: string; + private applicationTokenCredentials: ApplicationTokenCredentials; + // Add an entry here and separate function for each new environment private _environments = { 'AzureStack': 'azurestack' @@ -16,8 +20,7 @@ export class AzureRMEndpoint { this.endpoint = null; } - public async getEndpoint() { - let dataDeferred = Q.defer(); + public async getEndpoint(): Promise { if(!!this.endpoint) { return this.endpoint; } @@ -35,18 +38,21 @@ export class AzureRMEndpoint { } as AzureEndpoint; if(this.endpoint.environment != null && this.endpoint.environment.toLowerCase() == this._environments.AzureStack && ( !this.endpoint.environmentAuthorityUrl || !this.endpoint.activeDirectoryResourceID)) { - return await this._getAzureStackData(this.endpoint); + this.endpoint = await this._updateAzureStackData(this.endpoint); } else { this.endpoint.environmentAuthorityUrl = (!!this.endpoint.environmentAuthorityUrl) ? this.endpoint.environmentAuthorityUrl : "https://login.windows.net/"; this.endpoint.activeDirectoryResourceID = this.endpoint.url; } + + this.endpoint.applicationTokenCredentials = new ApplicationTokenCredentials(this.endpoint.servicePrincipalClientID, this.endpoint.tenantID, this.endpoint.servicePrincipalKey, + this.endpoint.url, this.endpoint.environmentAuthorityUrl, this.endpoint.activeDirectoryResourceID, this.endpoint.environment.toLowerCase() == constants.AzureEnvironments.AzureStack); } return this.endpoint; } - private _getAzureStackData(endpoint: AzureEndpoint) { + private async _updateAzureStackData(endpoint: AzureEndpoint): Promise { let dataDeferred = Q.defer(); let webRequest = new webClient.WebRequest(); webRequest.uri = `${endpoint.url}metadata/endpoints?api-version=2015-01-01`; @@ -55,55 +61,55 @@ export class AzureRMEndpoint { 'Content-Type': 'application/json' } - webClient.sendRequest(webRequest).then((response: webClient.WebResponse) => { - if(response.statusCode == 200) { - let result = response.body; - endpoint.graphEndpoint = result.graphEndpoint; - endpoint.galleryUrl = result.galleryUrl; - endpoint.portalEndpoint = result.portalEndpoint; - var authenticationData = result.authentication; - if(!!authenticationData) { - var loginEndpoint = authenticationData.loginEndpoint; - if(!!loginEndpoint) { - loginEndpoint += (loginEndpoint[loginEndpoint.length - 1] == "/") ? "" : "/"; - endpoint.activeDirectoryAuthority = loginEndpoint; - endpoint.environmentAuthorityUrl = loginEndpoint; - } - else { - // change to login endpoint - dataDeferred.reject(tl.loc('UnableToFetchAuthorityURL')); - } + let azureStackResult; + try { + let response: webClient.WebResponse = await webClient.sendRequest(webRequest); + if(response.statusCode != 200) { + tl.debug("Action: _updateAzureStackData, Response: " + JSON.stringify(response)); + throw new Error(response.statusCode + ' ' + response.statusMessage) + } - var audiences = authenticationData.audiences; - if(audiences && audiences.length > 0) { - endpoint.activeDirectoryResourceID = audiences[0]; - } + azureStackResult = response.body; + } + catch(error) { + throw new Error(tl.loc("FailedToFetchAzureStackDependencyData", error.toString())); + } - try { - var endpointUrl = endpoint.url; - endpointUrl += (endpointUrl[endpointUrl.length-1] == "/") ? "" : "/"; - var index = endpointUrl.indexOf('.'); - var domain = endpointUrl.substring(index+1); - domain = (domain.lastIndexOf("/") == domain.length-1) ? domain.substring(0, domain.length-1): domain; - endpoint.AzureKeyVaultDnsSuffix = ("vault" + domain).toLowerCase(); - endpoint.AzureKeyVaultServiceEndpointResourceId = ("https://vault." + domain).toLowerCase(); - } - catch(error) { - dataDeferred.reject(tl.loc("SpecifiedAzureRmEndpointIsInvalid", endpointUrl)); - } + endpoint.graphEndpoint = azureStackResult.graphEndpoint; + endpoint.galleryUrl = azureStackResult.galleryUrl; + endpoint.portalEndpoint = azureStackResult.portalEndpoint; + var authenticationData = azureStackResult.authentication; + if(!!authenticationData) { + var loginEndpoint = authenticationData.loginEndpoint; + if(!!loginEndpoint) { + loginEndpoint += (loginEndpoint[loginEndpoint.length - 1] == "/") ? "" : "/"; + endpoint.activeDirectoryAuthority = loginEndpoint; + endpoint.environmentAuthorityUrl = loginEndpoint; + } + else { + // change to login endpoint + throw new Error(tl.loc('UnableToFetchAuthorityURL')); + } - dataDeferred.resolve(endpoint); - } + var audiences = authenticationData.audiences; + if(audiences && audiences.length > 0) { + endpoint.activeDirectoryResourceID = audiences[0]; + } + try { + var endpointUrl = endpoint.url; + endpointUrl += (endpointUrl[endpointUrl.length-1] == "/") ? "" : "/"; + var index = endpointUrl.indexOf('.'); + var domain = endpointUrl.substring(index+1); + domain = (domain.lastIndexOf("/") == domain.length-1) ? domain.substring(0, domain.length-1): domain; + endpoint.AzureKeyVaultDnsSuffix = ("vault" + domain).toLowerCase(); + endpoint.AzureKeyVaultServiceEndpointResourceId = ("https://vault." + domain).toLowerCase(); } - else { - tl.debug("Action: initializeAzureStackData, Response: " + JSON.stringify(response)); - dataDeferred.reject(tl.loc("FailedToFetchAzureStackDependencyData", response.statusCode)); + catch(error) { + throw new Error(tl.loc("SpecifiedAzureRmEndpointIsInvalid", endpointUrl)); } - }, (error) => { - dataDeferred.reject(error); - }); - - return dataDeferred.promise; + } + + return endpoint; } } \ No newline at end of file diff --git a/Tasks/Common/azure-arm-rest/azureModels.ts b/Tasks/Common/azure-arm-rest/azureModels.ts index 86a7b5360ed5..37df22ae3b56 100644 --- a/Tasks/Common/azure-arm-rest/azureModels.ts +++ b/Tasks/Common/azure-arm-rest/azureModels.ts @@ -1,3 +1,5 @@ +import { ApplicationTokenCredentials } from "./azure-arm-common"; + export interface AzureBaseObject { name?: string; id: string; @@ -213,6 +215,7 @@ export interface AzureEndpoint { portalEndpoint?: string; AzureKeyVaultDnsSuffix?: string; AzureKeyVaultServiceEndpointResourceId?: string; + applicationTokenCredentials: ApplicationTokenCredentials; } export interface AzureAppServiceConfigurationDetails { diff --git a/Tasks/Common/azure-arm-rest/constants.ts b/Tasks/Common/azure-arm-rest/constants.ts new file mode 100644 index 000000000000..fc0f32226959 --- /dev/null +++ b/Tasks/Common/azure-arm-rest/constants.ts @@ -0,0 +1,7 @@ +export const AzureEnvironments = { + AzureStack: 'azurestack' +}; + +export const APPLICATION_INSIGHTS_EXTENSION_NAME: string = "Microsoft.ApplicationInsights.AzureWebSites"; + +export const productionSlot: string = "production"; \ No newline at end of file diff --git a/Tasks/Common/azure-arm-rest/package.json b/Tasks/Common/azure-arm-rest/package.json index 43d4e53a1384..35ad5402ce6a 100644 --- a/Tasks/Common/azure-arm-rest/package.json +++ b/Tasks/Common/azure-arm-rest/package.json @@ -15,7 +15,6 @@ "dependencies": { "q": "1.4.1", "vsts-task-lib": "2.0.5", - "typed-rest-client": "0.12.0", - "xml2js": "0.4.13" + "typed-rest-client": "0.12.0" } } diff --git a/Tasks/Common/azure-arm-rest/webClient.ts b/Tasks/Common/azure-arm-rest/webClient.ts index a293d8c9d9c7..9f0e511f5d55 100644 --- a/Tasks/Common/azure-arm-rest/webClient.ts +++ b/Tasks/Common/azure-arm-rest/webClient.ts @@ -70,6 +70,12 @@ export async function sendRequest(request: WebRequest, options?: WebRequestOptio } } +export function sleepFor(sleepDurationInSeconds): Promise { + return new Promise((resolve, reject) => { + setTimeout(resolve, sleepDurationInSeconds * 1000); + }); +} + async function sendRequestInternal(request: WebRequest): Promise { tl.debug(util.format("[%s]%s", request.method, request.uri)); var response: httpClient.HttpClientResponse = await httpCallbackClient.request(request.method, request.uri, request.body, request.headers); @@ -95,9 +101,3 @@ async function toWebResponse(response: httpClient.HttpClientResponse): Promise { - return new Promise((resolve, reject) => { - setTimeout(resolve, sleepDurationInSeconds * 1000); - }); -} \ No newline at end of file