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

AzureFunctionAppV2 Changes for Flex and fix race condition. #20191

Merged
merged 20 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,6 @@
"loc.messages.SyncingFunctionTriggers": "Die Trigger für die Funktions-App werden synchronisiert.",
"loc.messages.SyncFunctionTriggersSuccess": "Die Triggersynchronisierung für die Funktions-App wurde erfolgreich abgeschlossen.",
"loc.messages.UpdatedRunFromPackageSettings": "Die WEBSITE_RUN_FROM_PACKAGE-Anwendungseinstellung wurde auf \"%s\" aktualisiert.",
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "Die Option \"Bereitstellungstyp\" gilt nicht für Linux-Verbrauch."
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "Die Option \"Bereitstellungstyp\" gilt nicht für Linux-Verbrauch.",
"loc.messages.DeploymentTypeNotSupportedForFlexConsumption" : "Die Option \"Bereitstellungstyp\" gilt nicht für Flex-Verbrauch."
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,7 @@
"loc.messages.SyncingFunctionTriggers": "Syncing triggers for function app",
"loc.messages.SyncFunctionTriggersSuccess": "Sync triggers for function app completed successfully",
"loc.messages.UpdatedRunFromPackageSettings": "Updated WEBSITE_RUN_FROM_PACKAGE Application setting to %s",
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "The Deployment Type option does not apply for Linux Consumption."
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "The Deployment Type option does not apply for Linux Consumption.",
"loc.messages.DeploymentTypeNotSupportedForFlexConsumption" : "The Deployment Type option does not apply for Flex Consumption"

}
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,7 @@
"loc.messages.SyncingFunctionTriggers": "Sincronizando los desencadenadores para la aplicación de funciones",
"loc.messages.SyncFunctionTriggersSuccess": "La sincronización de los desencadenadores de la aplicación de funciones se ha completado correctamente.",
"loc.messages.UpdatedRunFromPackageSettings": "Se ha actualizado la configuración de aplicación de WEBSITE_RUN_FROM_PACKAGE a %s",
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "La opción de Tipo de implementación no se aplica para el Consumo para Linux."
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "La opción de Tipo de implementación no se aplica para el Consumo para Linux.",
"loc.messages.DeploymentTypeNotSupportedForFlexConsumption" : "La opción de Tipo de implementación no se aplica para el Consumo para Flex."

}
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,6 @@
"loc.messages.SyncingFunctionTriggers": "Synchronisation des déclencheurs pour l'application de fonction",
"loc.messages.SyncFunctionTriggersSuccess": "Synchronisation réussie des déclencheurs de l'application de fonction",
"loc.messages.UpdatedRunFromPackageSettings": "Mise à jour effectuée du paramètre d'application WEBSITE_RUN_FROM_PACKAGE vers %s",
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "L’option Type de déploiement ne s’applique pas à consommation Linux."
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "L’option Type de déploiement ne s’applique pas à consommation Linux.",
"loc.messages.DeploymentTypeNotSupportedForFlexConsumption": "L’option Type de déploiement ne s’applique pas à consommation Flex."
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,6 @@
"loc.messages.SyncingFunctionTriggers": "Sincronizzazione dei trigger per l'app per le funzioni",
"loc.messages.SyncFunctionTriggersSuccess": "La sincronizzazione dei trigger per l'app per le funzioni è stata completata",
"loc.messages.UpdatedRunFromPackageSettings": "L'impostazione applicazione WEBSITE_RUN_FROM_PACKAGE è stata aggiornata in %s",
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "L'opzione Tipo di distribuzione non è valida per a consumo per Linux."
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "L'opzione Tipo di distribuzione non è valida per a consumo per Linux.",
"loc.messages.DeploymentTypeNotSupportedForFlexConsumption": "L'opzione Tipo di distribuzione non è valida per a consumo per Flex."
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,6 @@
"loc.messages.SyncingFunctionTriggers": "関数アプリのトリガーを同期しています",
"loc.messages.SyncFunctionTriggersSuccess": "関数アプリの同期トリガーが正常に完了しました",
"loc.messages.UpdatedRunFromPackageSettings": "WEBSITE_RUN_FROM_PACKAGE アプリケーション設定が %s に更新されました",
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "[展開の種類] オプションは、Linux 従量課金プランには適用されません。"
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "[展開の種類] オプションは、Linux 従量課金プランには適用されません。",
"loc.messages.DeploymentTypeNotSupportedForFlexConsumption": "[展開の種類] オプションは、Flex 従量課金プランには適用されません。"
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,6 @@
"loc.messages.SyncingFunctionTriggers": "함수 앱의 트리거를 동기화하는 중",
"loc.messages.SyncFunctionTriggersSuccess": "함수 앱의 트리거 동기화를 완료했습니다.",
"loc.messages.UpdatedRunFromPackageSettings": "WEBSITE_RUN_FROM_PACKAGE 애플리케이션 설정을 %s(으)로 업데이트했습니다.",
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "배포 유형 옵션은 Linux 사용에 적용되지 않습니다."
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "배포 유형 옵션은 Linux 사용에 적용되지 않습니다.",
"loc.messages.DeploymentTypeNotSupportedForFlexConsumption": "배포 유형 옵션은 Flex 사용에 적용되지 않습니다."
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,5 @@
"loc.messages.SyncingFunctionTriggers": "Идет синхронизация триггеров для приложения-функции",
"loc.messages.SyncFunctionTriggersSuccess": "Синхронизация триггеров для приложения-функции успешно завершена.",
"loc.messages.UpdatedRunFromPackageSettings": "Параметр приложения WEBSITE_RUN_FROM_PACKAGE изменен на %s",
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "Параметр \"Тип развертывания\" не применяется для Потребление Linux."
}
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "Параметр \"Тип развертывания\" не применяется для Потребление Linux.",
"loc.messages.DeploymentTypeNotSupportedForFlexConsumption": "Параметр \"Тип развертывания\" не применяется для Потребление Flex.",
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,6 @@
"loc.messages.SyncingFunctionTriggers": "正在同步函数应用的触发器",
"loc.messages.SyncFunctionTriggersSuccess": "已成功完成函数应用的触发器同步",
"loc.messages.UpdatedRunFromPackageSettings": "已将 WEBSITE_RUN_FROM_PACKAGE 应用程序设置更新为 %s",
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "部署类型选项不适用于 Linux 消耗计划。"
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "部署类型选项不适用于 Linux 消耗计划。",
"loc.messages.DeploymentTypeNotSupportedForFlexConsumption": "部署类型选项不适用于 Flex 消耗计划。"
}
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,6 @@
"loc.messages.SyncingFunctionTriggers": "正在同步函數應用程式的觸發程序",
"loc.messages.SyncFunctionTriggersSuccess": "已成功完成函數應用程式的同步處理觸發程序",
"loc.messages.UpdatedRunFromPackageSettings": "已將 WEBSITE_RUN_FROM_PACKAGE 應用程式設定更新為 %s",
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "部署類型選項不適用於 Linux 使用量。"
"loc.messages.DeploymentTypeNotSupportedForLinuxConsumption": "部署類型選項不適用於 Linux 使用量。",
"loc.messages.DeploymentTypeNotSupportedForFlexConsumption": "部署類型選項不適用於 Flex 使用量。"
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class AzureRmWebAppDeploymentProvider implements IWebAppDeploymentProvide

public async UpdateDeploymentStatus(isDeploymentSuccess: boolean) {
await addReleaseAnnotation(this.taskParams.azureEndpoint, this.appService, isDeploymentSuccess);
if (this.kuduServiceUtility) {
if (this.kuduServiceUtility && !this.taskParams.isFlexConsumption) {
this.activeDeploymentID = await this.kuduServiceUtility.updateDeploymentStatus(isDeploymentSuccess, null, {'type': 'Deployment', slotName: this.appService.getSlot()});
tl.debug('Active DeploymentId :'+ this.activeDeploymentID);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ export class BuiltInLinuxWebAppDeploymentProvider extends AzureRmWebAppDeploymen
linuxFunctionAppSetting = linuxFunctionAppSetting + removeRunFromZipAppSetting;
}
var customApplicationSetting = ParameterParser.parse(linuxFunctionAppSetting);
isNewValueUpdated = await this.appServiceUtility.updateAndMonitorAppSettings(customApplicationSetting);
isNewValueUpdated = await this.appServiceUtilityExt.updateAndMonitorAppSettings(customApplicationSetting);

// add Output for isNewValueUpdated
tl.debug(" Ouutput the value for App Settings isNewValueUpdated : " + isNewValueUpdated);
FinVamp1 marked this conversation as resolved.
Show resolved Hide resolved

if(!isNewValueUpdated) {
await this.kuduServiceUtility.warmpUp();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { IWebAppDeploymentProvider } from './IWebAppDeploymentProvider';
import { WindowsWebAppZipDeployProvider } from './WindowsWebAppZipDeployProvider';
import { WindowsWebAppRunFromZipProvider } from './WindowsWebAppRunFromZipProvider';
import { ConsumptionWebAppDeploymentProvider } from './ConsumptionWebAppDeploymentProvider';
import { FlexConsumptionWebAppDeploymentProvider } from './FlexConsumptionWebAppDeploymentProvider';

export class DeploymentFactory {

Expand All @@ -20,7 +21,11 @@ export class DeploymentFactory {
tl.debug("Deployment started for linux app service");
if(this._taskParams.isConsumption) {
return new ConsumptionWebAppDeploymentProvider(this._taskParams);
} else {
}
else if(this._taskParams.isFlexConsumption) {
return new FlexConsumptionWebAppDeploymentProvider(this._taskParams);
}
else {
return new BuiltInLinuxWebAppDeploymentProvider(this._taskParams);
}
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import tl = require('azure-pipelines-task-lib/task');
import Q = require('q');
var webCommonUtility = require('azure-pipelines-tasks-webdeployment-common/utility');
var zipUtility = require('azure-pipelines-tasks-webdeployment-common/ziputility');
var azureStorage = require('azure-storage');
import { AzureAppService } from 'azure-pipelines-tasks-azure-arm-rest/azure-arm-app-service';
import { AzureAppServiceUtility } from 'azure-pipelines-tasks-azure-arm-rest/azureAppServiceUtility';
import { applyTransformations } from 'azure-pipelines-tasks-webdeployment-common/fileTransformationsUtility';
import { sleepFor } from 'azure-pipelines-tasks-azure-arm-rest/webClient';
import { PackageType } from 'azure-pipelines-tasks-webdeployment-common/packageUtility';
import * as ParameterParser from 'azure-pipelines-tasks-webdeployment-common/ParameterParserUtility';
import { AzureAppServiceUtilityExt } from '../operations/AzureAppServiceUtilityExt';
import { AzureRmWebAppDeploymentProvider } from './AzureRmWebAppDeploymentProvider';
import { Kudu } from 'azure-pipelines-tasks-azure-arm-rest/azure-arm-app-service-kudu';
import { KuduServiceUtility } from '../operations/KuduServiceUtility';

export class FlexConsumptionWebAppDeploymentProvider extends AzureRmWebAppDeploymentProvider {

public async PreDeploymentStep() {
this.appService = new AzureAppService(this.taskParams.azureEndpoint, this.taskParams.ResourceGroupName, this.taskParams.WebAppName,
this.taskParams.SlotName, this.taskParams.WebAppKind);
this.appServiceUtility = new AzureAppServiceUtility(this.appService, "AzureFunctionAppDeployment");
this.appServiceUtilityExt = new AzureAppServiceUtilityExt(this.appService);
this.kuduService = await this.appServiceUtility.getKuduService();
this.kuduServiceUtility = new KuduServiceUtility(this.kuduService);
}

public async DeployWebAppStep() {
let deploymentMethodtelemetry = '{"deploymentMethod":" Deployment for Flex Consumption"}';
console.log("##vso[telemetry.publish area=TaskDeploymentMethod;feature=AzureFunctionAppDeployment]" + deploymentMethodtelemetry);
// The Task Parms DeploymentType is not used in this deployment provider
if(this.taskParams.DeploymentType != null){
console.log(tl.loc('DeploymentTypeNotSupportedForFlexConsumption'));
}
let userDefinedAppSettings = this._getUserDefinedAppSettings();
var webPackage = await applyTransformations(this.taskParams.Package.getPath(), this.taskParams.WebConfigParameters, this.taskParams.Package.getPackageType());
if(tl.stats(webPackage).isDirectory()) {
let tempPackagePath = webCommonUtility.generateTemporaryFolderOrZipPath(tl.getVariable('AGENT.TEMPDIRECTORY'), false);
webPackage = await zipUtility.archiveFolder(webPackage, "", tempPackagePath);
tl.debug("Compressed folder into zip " + webPackage);
}
// constructing Query Parameters

var remoteBuild = "false";

let queryParameters:Array<string> = [
'remoteBuild=' + remoteBuild,
'deployer=' + 'VSTS_FUNCTIONS_V2'
];

tl.debug("Initiated deployment via kudu service for functionapp package : ");
await this.kuduServiceUtility.oneDeployFlex(webPackage, queryParameters);
await this.PostDeploymentStep();
}


private _getUserDefinedAppSettings() {
let userDefinedAppSettings = {};
if(this.taskParams.AppSettings) {
var customApplicationSettings = ParameterParser.parse(this.taskParams.AppSettings);
for(var property in customApplicationSettings) {
if(!!customApplicationSettings[property] && customApplicationSettings[property].value !== undefined) {
userDefinedAppSettings[property] = customApplicationSettings[property].value;
}
}
}

return userDefinedAppSettings;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ export class WindowsWebAppRunFromZipProvider extends AzureRmWebAppDeploymentProv

var addCustomApplicationSetting = ParameterParser.parse(runFromZipAppSetting);
var deleteCustomApplicationSetting = ParameterParser.parse(oldRunFromZipAppSetting);
var isNewValueUpdated: boolean = await this.appServiceUtility.updateAndMonitorAppSettings(addCustomApplicationSetting, deleteCustomApplicationSetting);
var isNewValueUpdated: boolean = await this.appServiceUtilityExt.updateAndMonitorAppSettings(addCustomApplicationSetting, deleteCustomApplicationSetting);

// add Output for isNewValueUpdated
tl.debug(" Ouutput the value for App Settings isNewValueUpdated : " + isNewValueUpdated);


if(!isNewValueUpdated) {
await this.kuduServiceUtility.warmpUp();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ export class WindowsWebAppZipDeployProvider extends AzureRmWebAppDeploymentProvi
tl.debug("Initiated deployment via kudu service for webapp package : ");

var deleteApplicationSetting = ParameterParser.parse(removeRunFromZipAppSetting)
var isNewValueUpdated: boolean = await this.appServiceUtility.updateAndMonitorAppSettings(null, deleteApplicationSetting);
var isNewValueUpdated: boolean = await this.appServiceUtilityExt.updateAndMonitorAppSettings(null, deleteApplicationSetting);
// add Output for isNewValueUpdated
tl.debug(" Ouutput the value for App Settings isNewValueUpdated : " + isNewValueUpdated);

if(!isNewValueUpdated) {
await this.kuduServiceUtility.warmpUp();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ var glob = require("glob");
import * as os from "os";
import { AzureAppService } from 'azure-pipelines-tasks-azure-arm-rest/azure-arm-app-service';
import { AzureDeployPackageArtifactAlias } from 'azure-pipelines-tasks-azure-arm-rest/constants';
import webClient = require('azure-pipelines-tasks-azure-arm-rest/webClient');
import { Kudu } from 'azure-pipelines-tasks-azure-arm-rest/azure-arm-app-service-kudu';
import { AzureAppServiceUtility as AzureAppServiceUtilityCommon } from 'azure-pipelines-tasks-azure-arm-rest/azureAppServiceUtility';


export class AzureAppServiceUtilityExt {
private _appService: AzureAppService;
Expand Down Expand Up @@ -57,6 +61,67 @@ export class AzureAppServiceUtilityExt {
}
}

public async getKuduService(): Promise<Kudu> {

const utility = new AzureAppServiceUtilityCommon(this._appService);
return await utility.getKuduService();

}

// Adding checking for property to be updated in Kudu first and then in App Service
public async updateAndMonitorAppSettings(addProperties: any, deleteProperties?: any): Promise<boolean> {
for(var property in addProperties) {
if(!!addProperties[property] && addProperties[property].value !== undefined) {
addProperties[property] = addProperties[property].value;
}
}

console.log(tl.loc('UpdatingAppServiceApplicationSettings', JSON.stringify(addProperties)));
var isNewValueUpdated: boolean = await this._appService.patchApplicationSettings(addProperties, deleteProperties);

if(!!isNewValueUpdated) {
console.log(tl.loc('UpdatedAppServiceApplicationSettings'));
}
else {
console.log(tl.loc('AppServiceApplicationSettingsAlreadyPresent'));
return isNewValueUpdated;
}

var kuduService = await this.getKuduService();
var noOftimesToIterate: number = 12;
tl.debug('retrieving values from Kudu service to check if new values are updated');
while(noOftimesToIterate > 0) {
var kuduServiceAppSettings = await kuduService.getAppSettings();
var propertiesChanged: boolean = true;
for(var property in addProperties) {
if(kuduServiceAppSettings[property] != addProperties[property]) {
tl.debug('New properties are not updated in Kudu service :(');
propertiesChanged = false;
break;
}
}
for(var property in deleteProperties) {
if(kuduServiceAppSettings[property]) {
tl.debug('Deleted properties are not reflected in Kudu service :(');
propertiesChanged = false;
break;
}
}

if(propertiesChanged) {
tl.debug('New properties are updated in Kudu service.');
console.log(tl.loc('UpdatedAppServiceApplicationSettings'));
return isNewValueUpdated;
}

noOftimesToIterate -= 1;
await webClient.sleepFor(5);
}

tl.debug('Timing out from app settings check');
return isNewValueUpdated;
}

public async updateConnectionStrings(addProperties: any): Promise<boolean> {
var connectionStringProperties = {};
for(var property in addProperties) {
Expand Down
Loading
Loading