Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Azure App service Manage & Deploy Task Spec changes #4125

Merged
merged 18 commits into from
May 4, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions Tasks/AzureAppServiceManage/azureappservicemanage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ async function updateKuduDeploymentLog(endPoint, webAppName, resourceGroupName,
}
}

async function waitForAppServiceToStart(endPoint, resourceGroupName, webAppName, specifySlotFlag, slotName) {
var appServiceDetails = await azureRmUtil.getAppServiceDetails(endPoint, resourceGroupName, webAppName, specifySlotFlag, slotName);
if(appServiceDetails.hasOwnProperty("properties") && appServiceDetails.properties.hasOwnProperty("state"))
{
tl.debug('App Service State : ' + appServiceDetails.properties.state);
while(!(appServiceDetails.properties.state == "Running" || appServiceDetails.properties.state == "running"))
{
appServiceDetails = await azureRmUtil.getAppServiceDetails(endPoint, resourceGroupName, webAppName, specifySlotFlag, slotName);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to add a wait() here and retry after certain intervals?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait is actually not required as we just poll for the state of the app and not on any operation.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check for hasProperty("properties") and hasProperty("state") after getting appServiceDetails in while loop

tl.debug('App Service State : ' + appServiceDetails.properties.state);
}
tl.debug('App Service is in Running State');
}
}

async function run() {
try {
tl.setResourcePath(path.join( __dirname, 'task.json'));
Expand Down Expand Up @@ -62,14 +76,7 @@ async function run() {
switch(action) {
case "Start Azure App Service": {
console.log(await azureRmUtil.startAppService(endPoint, resourceGroupName, webAppName, specifySlotFlag, slotName));
var appServiceDetails = await azureRmUtil.getAppServiceDetails(endPoint, resourceGroupName, webAppName, specifySlotFlag, slotName);
if(appServiceDetails.hasOwnProperty("properties") && appServiceDetails.properties.hasOwnProperty("state"))
{
while(!(appServiceDetails.properties.state == "Running" || appServiceDetails.properties.state == "running"))
{
appServiceDetails = await azureRmUtil.getAppServiceDetails(endPoint, resourceGroupName, webAppName, specifySlotFlag, slotName);
}
}
await waitForAppServiceToStart(endPoint, resourceGroupName, webAppName, specifySlotFlag, slotName);
break;
}
case "Stop Azure App Service": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we wait for Stop as well?

Copy link
Contributor Author

@vincent1173 vincent1173 Apr 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stop actually stops the App Service to accept any request. We should be good here.

Expand All @@ -88,6 +95,7 @@ async function run() {
}
case "Restart Azure App Service": {
console.log(await azureRmUtil.restartAppService(endPoint, resourceGroupName, webAppName, specifySlotFlag, slotName));
await waitForAppServiceToStart(endPoint, resourceGroupName, webAppName, specifySlotFlag, slotName);
break;
}
case "Swap Slots": {
Expand Down
2 changes: 1 addition & 1 deletion Tasks/AzureAppServiceManage/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"version": {
"Major": 0,
"Minor": 2,
"Patch": 7
"Patch": 8
},
"minimumAgentVersion": "1.102.0",
"instanceNameFormat": "$(Action): $(WebAppName)",
Expand Down
2 changes: 1 addition & 1 deletion Tasks/AzureAppServiceManage/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"version": {
"Major": 0,
"Minor": 2,
"Patch": 7
"Patch": 8
},
"minimumAgentVersion": "1.102.0",
"instanceNameFormat": "ms-resource:loc.instanceNameFormat",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,8 @@
"loc.messages.ScriptStatusTimeout": "Unable to fetch script status due to timeout.",
"loc.messages.PollingForFileTimeOut": "Unable to fetch script status due to timeout. You can increase the timeout limit by setting 'appservicedeploy.retrytimeout' variable to number of minutes required.",
"loc.messages.InvalidPollOption": "Invalid polling option provided: %s.",
"loc.messages.MissingWebConfigParameters": "Some web.config parameters are missing"
"loc.messages.MissingAppTypeWebConfigParameters": "-appType attribute is missing in Web.config parameters.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is apptype the only config param that can be missing? Should we also report if other required parameters are not present?

Copy link
Contributor Author

@vincent1173 vincent1173 Apr 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can override with default values. I ll update it in this PR.

"loc.messages.AutoDetectDjangoSettingsFailed": "Unable to detect DJANGO_SETTINGS_MODULE (settings.py). Please ensure that the file exists or provide the correct path in Web.config parameters input in the following format '<folder_name>.settings'",
"loc.messages.FailedToApplyTransformation": "Unable to apply transformation for the given package. Please follow below steps to debug the issue. \n1. Transformation is applied for MSBuild generated packge during build time. Remove <DependentUpon> Tag for each config file. \n2. Ensure that the config file and transformation file are present in the same folder inside the package.",
"loc.messages.AutoParameterizationMessage": "ConnectionString attributes in Web.config is parameterized by default. Please be aware that transformation has no effect on connectioString attributes as the same value is overridden during deployment. You can disable the auto parameterization by setting /p:AutoParameterizationWebConfigConnectionStrings=False during MSBuild package generation."
}
13 changes: 13 additions & 0 deletions Tasks/AzureRmWebAppDeployment/Tests/L0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ describe('AzureRmWebAppDeployment Suite', function() {
expectedOut = 'Successfully updated scmType to VSTSRM';
assert(tr.stdout.search(expectedOut) > 0, 'should have said: ' + expectedOut);
assert(tr.succeeded, 'task should have succeeded');
assert(tr.stdout.search('loc_mock_AutoParameterizationMessage'), 'Should have provided message for MSBuild package');
done();
});

Expand All @@ -291,6 +292,18 @@ describe('AzureRmWebAppDeployment Suite', function() {
assert(tr.failed, 'task should have failed');
done();
});

it('Fails if XML Transformation throws error (Mock) for MSBuild package', (done) => {
this.timeout(1000);
let tp = path.join(__dirname, 'L0XdtTransformationFailMSBuildPackage.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();

assert(tr.failed, 'task should have failed');
assert(tr.stderr.search('loc_mock_FailedToApplyTransformation'), 'Should have provided proper errror message for MSBuild package');
assert(tr.stdout.search('loc_mock_AutoParameterizationMessage'), 'Should have provided message for MSBuild package');
done();
});
}
else {
it('Runs KuduDeploy successfully with default inputs on non-windows agent', (done) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ tr.registerMock('./msdeployutility.js', {
});

tr.registerMock('azurerest-common/azurerestutility.js', {
testAzureWebAppAvailability: function() {
console.log('App Service availability check.');
},
getAzureRMWebAppPublishProfile: function(SPN, webAppName, resourceGroupName, deployToSlotFlag, slotName) {
var mockPublishProfile = {
profileName: 'mytestapp - Web Deploy',
Expand Down Expand Up @@ -222,12 +225,6 @@ tr.registerMock('webdeployment-common/utility.js', {
}
});

tr.registerMock('webdeployment-common/generatewebconfig.js', {
generateWebConfigFile: function(filePath, templatePath, data) {
return;
}
});

tr.registerMock('./parameterparser', {
parse: function (data) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ tr.registerMock('webdeployment-common/utility.js', {
"webDeployPkg": "DefaultWorkingDirectory\\temp_web_package.zip",
"tempPackagePath": "DefaultWorkingDirectory\\temp_web_package.zip"
};
},
isMSDeployPackage: function() {
return true;
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ tr.registerMock('webdeployment-common/utility.js', {
"webDeployPkg": "DefaultWorkingDirectory\\temp_web_package.zip",
"tempPackagePath": "DefaultWorkingDirectory\\temp_web_package.zip"
};
},
isMSDeployPackage: function() {
return false;
}
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
import ma = require('vsts-task-lib/mock-answer');
import tmrm = require('vsts-task-lib/mock-run');
import path = require('path');

let taskPath = path.join(__dirname, '..', 'azurermwebappdeployment.js');
let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);
tr.setInput('ConnectedServiceName', 'AzureRMSpn');
tr.setInput('WebAppName', 'mytestapp');
tr.setInput('Package', 'webAppPkg.zip');
tr.setInput('UseWebDeploy', 'true');
tr.setInput('XmlTransformation', 'true');

process.env['TASK_TEST_TRACE'] = 1;
process.env["ENDPOINT_AUTH_AzureRMSpn"] = "{\"parameters\":{\"serviceprincipalid\":\"spId\",\"serviceprincipalkey\":\"spKey\",\"tenantid\":\"tenant\"},\"scheme\":\"ServicePrincipal\"}";
process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONNAME"] = "sName";
process.env["ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID"] = "sId";
process.env["AZURE_HTTP_USER_AGENT"] = "TFS_useragent";
process.env["SYSTEM_DEFAULTWORKINGDIRECTORY"] = "DefaultWorkingDirectory";
process.env["BUILD_SOURCEVERSION"] = "46da24f35850f455185b9188b4742359b537076f";
process.env["BUILD_BUILDID"] = 1,
process.env["RELEASE_RELEASEID"] = 1;
process.env["BUILD_BUILDNUMBER"] = 1;
process.env["RELEASE_RELEASENAME"] = "Release-1";
process.env["BUILD_REPOSITORY_PROVIDER"] = "TfsGit";
process.env["BUILD_REPOSITORY_NAME"] = "MyFirstProject";
process.env["SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"] = "https://abc.visualstudio.com/";
process.env["SYSTEM_TEAMPROJECT"] = "MyFirstProject";
process.env["BUILD_SOURCEVERISONAUTHOR"] = "author";
process.env["RELEASE_RELEASEURI"] = "vstfs:///ReleaseManagement/Release/1";
process.env["AGENT_NAME"] = "author";

// provide answers for task mock
let a: ma.TaskLibAnswers = <ma.TaskLibAnswers>{
"which": {
"cmd": "cmd",
"DefaultWorkingDirectory/ctt/ctt.exe": "DefaultWorkingDirectory/ctt/ctt.exe"
},
"stats": {
"webAppPkg.zip": {
"isFile": true
}
},
"osType": {
"osType": "Windows"
},
"checkPath": {
"cmd": true,
"webAppPkg.zip": true,
"webAppPkg": true,
"DefaultWorkingDirectory/ctt/ctt.exe": true
},
"exec": {
"cmd /C DefaultWorkingDirectory\\msDeployCommand.bat": {
"code": 0,
"stdout": "Executed Successfully"
},
"cmd /C DefaultWorkingDirectory\\msDeployParam.bat": {
"code": 0,
"stdout": "Executed Successfully"
},
"DefaultWorkingDirectory/ctt/ctt.exe s:C:\\tempFolder\\web.config t:C:\\tempFolder\\web.Release.config d:C:\\tempFolder\\web.config pw": {
"code": 1,
"stderr": "ctt execution failed"
}
},
"exist": {
"webAppPkg.zip": true,
"webAppPkg": true
},
"findMatch": {
"webAppPkgPattern" : ["webAppPkg1", "webAppPkg2"],
"Invalid_webAppPkg" : [],
"webAppPkg.zip": ["webAppPkg.zip"],
"webAppPkg": ["webAppPkg"],
"**/*.config": ["C:\\tempFolder\\web.config"]
},
"getVariable": {
"ENDPOINT_AUTH_AzureRMSpn": "{\"parameters\":{\"serviceprincipalid\":\"spId\",\"serviceprincipalkey\":\"spKey\",\"tenantid\":\"tenant\"},\"scheme\":\"ServicePrincipal\"}",
"ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONNAME": "sName",
"ENDPOINT_DATA_AzureRMSpn_SUBSCRIPTIONID": "sId",
"AZURE_HTTP_USER_AGENT": "TFS_useragent",
"System.DefaultWorkingDirectory": "DefaultWorkingDirectory",
"build.sourceVersion": "46da24f35850f455185b9188b4742359b537076f",
"build.buildId": 1,
"release.releaseId": 1,
"build.buildNumber": 1,
"release.releaseName": "Release-1",
"build.repository.provider": "TfsGit",
"build.repository.name": "MyFirstProject",
"system.TeamFoundationCollectionUri": "https://abc.visualstudio.com/",
"system.teamProject": "MyFirstProject",
"build.sourceVersionAuthor": "author",
"release.releaseUri": "vstfs:///ReleaseManagement/Release/1",
"agent.name": "agent"
}
};

import mockTask = require('vsts-task-lib/mock-task');
var kuduDeploymentLog = require('azurerest-common/kududeploymentstatusutility.js');
var msDeployUtility = require('webdeployment-common/msdeployutility.js');
tr.registerMock('./msdeployutility.js', {
getMSDeployCmdArgs : msDeployUtility.getMSDeployCmdArgs,
getMSDeployFullPath : function() {
var msDeployFullPath = "msdeploypath\\msdeploy.exe";
return msDeployFullPath;
}
});

tr.registerMock('azurerest-common/azurerestutility.js', {
getAzureRMWebAppPublishProfile: function(SPN, webAppName, resourceGroupName, deployToSlotFlag, slotName) {
var mockPublishProfile = {
profileName: 'mytestapp - Web Deploy',
publishMethod: 'MSDeploy',
publishUrl: 'mytestappKuduUrl',
msdeploySite: 'mytestapp',
userName: '$mytestapp',
userPWD: 'mytestappPwd',
destinationAppUrl: 'mytestappUrl',
SQLServerDBConnectionString: '',
mySQLDBConnectionString: '',
hostingProviderForumLink: '',
controlPanelLink: '',
webSystem: 'WebSites'
};
if(deployToSlotFlag) {
mockPublishProfile.profileName = 'mytestapp-' + slotName + ' - Web Deploy';
mockPublishProfile.publishUrl = 'mytestappKuduUrl-' + slotName;
mockPublishProfile.msdeploySite = 'mytestapp__' + slotName;
mockPublishProfile.userName = '$mytestapp__' + slotName;
mockPublishProfile.userPWD = 'mytestappPwd';
mockPublishProfile.destinationAppUrl = 'mytestappUrl-' + slotName;
}
return mockPublishProfile;
},
getAzureRMWebAppConfigDetails: function(SPN, webAppName, resourceGroupName, deployToSlotFlag, slotName) {
var config = {
id: 'appid',
properties: {
virtualApplications: [ ['Object'], ['Object'], ['Object'] ],
scmType: "None"
}
}

return config;
},
updateDeploymentStatus: function(publishingProfile, isDeploymentSuccess ) {
if(isDeploymentSuccess) {
console.log('Updated history to kudu');
}
else {
console.log('Failed to update history to kudu');
}
var webAppPublishKuduUrl = publishingProfile.publishUrl;
var requestDetails = kuduDeploymentLog.getUpdateHistoryRequest(webAppPublishKuduUrl, isDeploymentSuccess);
requestDetails["requestBody"].author = 'author';
console.log("kudu log requestBody is:" + JSON.stringify(requestDetails["requestBody"]));
},
getResourceGroupName: function (SPN, webAppName) {
return "foobar";
},
getWebAppAppSettings : function (SPN, webAppName: string, resourceGroupName: string, deployToSlotFlag: boolean, slotName: string){
var appSettings = {
properties : {
MSDEPLOY_RENAME_LOCKED_FILES : '1'
}
};
return appSettings;
},
updateWebAppAppSettings : function (){
return true;
},
updateAzureRMWebAppConfigDetails: function() {
console.log("Successfully updated scmType to VSTSRM");
}
});

tr.registerMock('webdeployment-common/utility.js', {
isInputPkgIsFolder: function() {
return false;
},
fileExists: function() {
return true;
},
canUseWebDeploy: function() {
return true;
},
findfiles: function() {
return ['webDeployPkg']
},
generateTemporaryFolderForDeployment: function() {
return 'temp_web_package_random_path';
},
archiveFolderForDeployment: function() {
return {
"webDeployPkg": "DefaultWorkingDirectory\\temp_web_package.zip",
"tempPackagePath": "DefaultWorkingDirectory\\temp_web_package.zip"
};
},
isMSDeployPackage: function() {
return false;
}
});

tr.registerMock('path', {
win32: {
basename: function(filePath, extension) {
return path.win32.basename(filePath, extension);
}
},
join: function() {
if(arguments[arguments.length -1] === 'ctt.exe') {
return 'DefaultWorkingDirectory/ctt/ctt.exe';
}
var args = [];
for(var i=0; i < arguments.length; i += 1) {
args.push(arguments[i]);
}
return args.join('\\');
},
dirname: path.dirname
});

tr.setAnswers(a);
tr.run();
Loading