Skip to content

Commit

Permalink
Azure Functions - Publishing enhancements (#587)
Browse files Browse the repository at this point in the history
  • Loading branch information
maartenba authored Apr 19, 2022
1 parent d916de2 commit 86c9d6f
Show file tree
Hide file tree
Showing 14 changed files with 237 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
<li>Compatibility with Rider 2022.1</li>
<li>Azure Functions: Run project against a function core tool version defined in project file (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/294">#294</a>)</li>
<li>Azure Functions: Support NCrontab 5-field expressions (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/571">#571</a>)</li>
<li>Azure Functions Host version can be selected from new project dialog (<a href="https://github.com/JetBrains/azure-tools-for-intellij/pull/568">#568</a>)</li>
<li>Azure Functions: Host version can be selected from new project dialog (<a href="https://github.com/JetBrains/azure-tools-for-intellij/pull/568">#568</a>)</li>
<li>Azure Functions: choose OS (Windows/Linux) when creating new function app (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/585">#585</a>)</li>
</ul>
<h4>Fixed bugs:</h4>
<ul>
<li>Azure Functions: Wrong version of core tools is downloaded on M1 (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/582">#582</a>)</li>
<li>Azure Functions: Set correct worker runtime when publishing (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/586">#586</a>)</li>
<li>SSLHandshakeException connecting to Azure Cloud Shell (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/579">#579</a>)</li>
</ul>
</html>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@
<li>Compatibility with Rider 2022.1</li>
<li>Azure Functions: Run project against a function core tool version defined in project file (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/294">#294</a>)</li>
<li>Azure Functions: Support NCrontab 5-field expressions (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/571">#571</a>)</li>
<li>Azure Functions Host version can be selected from new project dialog (<a href="https://github.com/JetBrains/azure-tools-for-intellij/pull/568">#568</a>)</li>
<li>Azure Functions: Host version can be selected from new project dialog (<a href="https://github.com/JetBrains/azure-tools-for-intellij/pull/568">#568</a>)</li>
<li>Azure Functions: choose OS (Windows/Linux) when creating new function app (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/585">#585</a>)</li>
</ul>
<h4>Fixed bugs:</h4>
<ul>
<li>Azure Functions: Wrong version of core tools is downloaded on M1 (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/582">#582</a>)</li>
<li>Azure Functions: Set correct worker runtime when publishing (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/586">#586</a>)</li>
<li>SSLHandshakeException connecting to Azure Cloud Shell (<a href="https://github.com/JetBrains/azure-tools-for-intellij/issues/579">#579</a>)</li>
</ul>
<p>[3.50.0-2021.3]</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ run_config.publish.form.function_app.existing_new_selector=Function App
run_config.publish.form.function_app.app_settings_panel_name=Run On Function App
run_config.publish.form.function_app.app_config_tab_name=App Configuration
run_config.publish.form.function_app.db_config_tab_name=Database Connection
run_config.publish.form.function_app.runtime_mismatch_warning=Selected Azure Function App runtime ''{0}'' mismatch with Project .Net Core Framework ''{1}''
run_config.publish.form.resource_group.header=Resource Group
run_config.publish.form.hosting_plan.header=Hosting Plan
run_config.publish.form.storage_account.header=Storage Account
Expand Down Expand Up @@ -322,6 +323,9 @@ run_config.run_function_app.debug.ignore.externalconsole=The 'Use external conso

# Process Events
process_event.publish.url=URL: {0}
process_event.publish.updating_runtime=Updating runtime to ''{0}'' (version ''{1}'')
process_event.publish.updating_runtime.linux=Linux runtime: ''{0}''
process_event.publish.updating_appsettings.scm_build=Updating application setting: SCM_DO_BUILD_DURING_DEPLOYMENT=false
process_event.publish.deploy_succeeded=Deploy succeeded.
process_event.publish.done=Done.
process_event.publish.connection_string.creating=Creating connection string with name ''{0}''...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ import com.intellij.notification.NotificationGroupManager
import com.intellij.notification.NotificationType
import com.intellij.notification.Notifications
import com.intellij.openapi.project.Project
import com.microsoft.azure.management.appservice.WebAppBase
import com.microsoft.azure.management.appservice.*
import com.microsoft.azure.management.sql.SqlDatabase
import com.microsoft.azure.toolkit.intellij.common.AzureRunProfileState
import com.microsoft.azuretools.core.mvp.model.AzureMvpModel
import com.microsoft.azuretools.core.mvp.model.appserviceplan.AzureAppServicePlanMvpModel
import com.microsoft.azuretools.core.mvp.model.database.AzureSqlDatabaseMvpModel
import com.microsoft.azuretools.core.mvp.model.functionapp.AzureFunctionAppMvpModel
import com.microsoft.azuretools.core.mvp.model.storage.AzureStorageAccountMvpModel
Expand Down Expand Up @@ -61,7 +62,6 @@ class FunctionAppRunState(project: Project, private val myModel: FunctionAppSett
companion object {
private const val TARGET_FUNCTION_NAME = "FunctionApp"
private const val TARGET_FUNCTION_DEPLOYMENT_SLOT_NAME = "FunctionDeploymentSlot"
private const val URL_FUNCTION_APP_WWWROOT = "/home/site/wwwroot"
}

override fun getDeployTarget(): String =
Expand All @@ -82,6 +82,7 @@ class FunctionAppRunState(project: Project, private val myModel: FunctionAppSett
AzureRiderSettings.VALUE_COLLECT_ARTIFACTS_TIMEOUT_MINUTES_DEFAULT) * 60000L

val app = getOrCreateFunctionAppFromConfiguration(myModel.functionAppModel, processHandler)
tryConfigureAzureFunctionRuntimeStack(app, subscriptionId, processHandler)
deployToAzureFunctionApp(project, publishableProject, app, processHandler, collectArtifactsTimeoutMs)

isFunctionAppCreated = true
Expand Down Expand Up @@ -125,6 +126,42 @@ class FunctionAppRunState(project: Project, private val myModel: FunctionAppSett
return FunctionAppDeployResult(app, database)
}

private fun tryConfigureAzureFunctionRuntimeStack(app: WebAppBase, subscriptionId: String, processHandler: RunProcessHandler) {
if (app !is FunctionApp) return

val functionRuntimeStack = myModel.functionAppModel.functionRuntimeStack
processHandler.setText(message("process_event.publish.updating_runtime", functionRuntimeStack.runtime(), functionRuntimeStack.version()))

if (myModel.functionAppModel.operatingSystem == OperatingSystem.LINUX) {
val appServicePlan = AzureAppServicePlanMvpModel
.getAppServicePlanById(subscriptionId, app.appServicePlanId())

processHandler.setText(message("process_event.publish.updating_runtime.linux", functionRuntimeStack.linuxFxVersionForDedicatedPlan))

// For Linux, we have to set the correct FunctionRuntimeStack
app.update()
.withExistingLinuxAppServicePlan(appServicePlan)
.withBuiltInImage(functionRuntimeStack)
.apply()

// For Linux dynamic (consumption) plan, we have to set SCM_DO_BUILD_DURING_DEPLOYMENT=false
if (appServicePlan.pricingTier() == FunctionAppPublishModel.dynamicPricingTier) {

processHandler.setText(message("process_event.publish.updating_appsettings.scm_build"))

app.update()
.withAppSetting("SCM_DO_BUILD_DURING_DEPLOYMENT", "false")
.apply()
}
} else {
// For Windows, we have to set the correct runtime and version
app.update()
.withRuntime(functionRuntimeStack.runtime())
.withRuntimeVersion(functionRuntimeStack.version())
.apply()
}
}

override fun onSuccess(result: FunctionAppDeployResult, processHandler: RunProcessHandler) {
processHandler.notifyComplete()

Expand Down Expand Up @@ -186,4 +223,4 @@ class FunctionAppRunState(project: Project, private val myModel: FunctionAppSett

Notifications.Bus.notify(notification, project)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2021 JetBrains s.r.o.
* Copyright (c) 2019-2022 JetBrains s.r.o.
*
* All rights reserved.
*
Expand Down Expand Up @@ -76,6 +76,7 @@ object FunctionAppDeployStateUtil {
val functionModelLog = StringBuilder("Create a new Function App with name '${model.appName}', ")
.append("isCreateResourceGroup: ").append(model.isCreatingResourceGroup).append(", ")
.append("resourceGroupName: ").append(model.resourceGroupName).append(", ")
.append("operatingSystem: ").append(model.operatingSystem).append(", ")
.append("isCreatingAppServicePlan: ").append(model.isCreatingAppServicePlan).append(", ")
.append("appServicePlanId: ").append(model.appServicePlanId).append(", ")
.append("appServicePlanName: ").append(model.appServicePlanName).append(", ")
Expand All @@ -102,6 +103,7 @@ object FunctionAppDeployStateUtil {
appName = model.appName,
isCreateResourceGroup = model.isCreatingResourceGroup,
resourceGroupName = model.resourceGroupName,
operatingSystem = model.operatingSystem,
isCreateAppServicePlan = model.isCreatingAppServicePlan,
appServicePlanId = model.appServicePlanId,
appServicePlanName = model.appServicePlanName,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2019-2020 JetBrains s.r.o.
* Copyright (c) 2019-2022 JetBrains s.r.o.
*
* All rights reserved.
*
Expand Down Expand Up @@ -40,6 +40,9 @@ import com.microsoft.intellij.ui.component.StorageAccountSelector
import com.microsoft.intellij.ui.component.appservice.AppNameComponent
import com.microsoft.intellij.ui.component.appservice.HostingPlanSelector
import com.microsoft.intellij.runner.functionapp.model.FunctionAppPublishModel
import com.microsoft.intellij.ui.component.appservice.OperatingSystemSelector
import com.microsoft.intellij.ui.extension.fillComboBox
import com.microsoft.intellij.ui.extension.setComponentsEnabled
import net.miginfocom.swing.MigLayout
import org.jetbrains.plugins.azure.RiderAzureBundle.message
import javax.swing.JPanel
Expand All @@ -55,20 +58,60 @@ class FunctionAppCreateNewComponent(lifetime: Lifetime) :
val pnlResourceGroup = ResourceGroupSelector(lifetime.createNested())
private val pnlResourceGroupHolder = HideableTitledPanel(message("run_config.publish.form.resource_group.header"), pnlResourceGroup, true)

val pnlOperatingSystem = OperatingSystemSelector()
private val pnlOperatingSystemHolder = HideableTitledPanel(message("run_config.publish.form.operating_system.header"), pnlOperatingSystem, true)

val pnlHostingPlan = HostingPlanSelector(lifetime.createNested())
private val pnlHostingPlanHolder = HideableTitledPanel(message("run_config.publish.form.hosting_plan.header"), pnlHostingPlan, true)

val pnlStorageAccount = StorageAccountSelector(lifetime.createNested())
private val pnlStorageAccountHolder = HideableTitledPanel(message("run_config.publish.form.storage_account.header"), pnlStorageAccount, true)

init {
initButtonGroupsState()

add(pnlAppName, "growx")
add(pnlSubscription, "growx")
add(pnlResourceGroupHolder, "growx")
add(pnlOperatingSystemHolder, "growx")
add(pnlHostingPlanHolder, "growx")
add(pnlStorageAccountHolder, "growx")
}

fun setOperatingSystemRadioButtons(isDotNetCore: Boolean) {
setComponentsEnabled(isDotNetCore, pnlOperatingSystem.rdoOperatingSystemLinux)

if (!isDotNetCore) {
pnlOperatingSystem.rdoOperatingSystemWindows.doClick()
toggleOperatingSystem(OperatingSystem.WINDOWS)
}
}

//region Button Group

private fun initButtonGroupsState() {
initOperatingSystemButtonGroup()
}

private fun initOperatingSystemButtonGroup() {
pnlOperatingSystem.rdoOperatingSystemWindows.addActionListener { toggleOperatingSystem(OperatingSystem.WINDOWS) }
pnlOperatingSystem.rdoOperatingSystemLinux.addActionListener { toggleOperatingSystem(OperatingSystem.LINUX) }
}

fun toggleOperatingSystem(operatingSystem: OperatingSystem) {
pnlHostingPlan.cbHostingPlan.fillComboBox(
filterAppServicePlans(operatingSystem, pnlHostingPlan.cachedAppServicePlan),
pnlHostingPlan.lastSelectedAppServicePlan
)

pnlHostingPlan.cbPricingTier.fillComboBox(
filterPricingTiers(pnlHostingPlan.cachedPricingTier),
pnlHostingPlan.lastSelectedAppServicePlan?.pricingTier()
)
}

//endregion Button Group

fun fillSubscription(subscriptions: List<Subscription>, defaultSubscription: Subscription? = null) =
pnlSubscription.fillSubscriptionComboBox(subscriptions, defaultSubscription)

Expand All @@ -79,13 +122,16 @@ class FunctionAppCreateNewComponent(lifetime: Lifetime) :
}

fun fillAppServicePlan(appServicePlans: List<AppServicePlan>, defaultAppServicePlanId: String? = null) =
pnlHostingPlan.fillAppServicePlan(filterAppServicePlans(appServicePlans), defaultAppServicePlanId)
pnlHostingPlan.fillAppServicePlan(
appServicePlans,
{ filterAppServicePlans(pnlOperatingSystem.deployOperatingSystem, it) },
defaultAppServicePlanId)

fun fillLocation(locations: List<Location>, defaultLocation: Region? = null) =
pnlHostingPlan.fillLocationComboBox(locations, defaultLocation)

fun fillPricingTier(pricingTiers: List<PricingTier>, defaultPricingTier: PricingTier? = null) =
pnlHostingPlan.fillPricingTier(updatePricingTiers(pricingTiers), defaultPricingTier)
pnlHostingPlan.fillPricingTier(filterPricingTiers(pricingTiers), defaultPricingTier)

fun fillStorageAccount(storageAccounts: List<StorageAccount>, defaultStorageAccountId: String? = null) =
pnlStorageAccount.fillStorageAccount(storageAccounts, defaultStorageAccountId)
Expand All @@ -96,9 +142,10 @@ class FunctionAppCreateNewComponent(lifetime: Lifetime) :
/**
* Filter App Service Plans to Operating System related values
*/
private fun filterAppServicePlans(appServicePlans: List<AppServicePlan>): List<AppServicePlan> {
private fun filterAppServicePlans(operatingSystem: OperatingSystem,
appServicePlans: List<AppServicePlan>): List<AppServicePlan> {
return appServicePlans
.filter { it.operatingSystem() == OperatingSystem.WINDOWS }
.filter { it.operatingSystem() == operatingSystem }
.sortedWith(compareBy({ it.operatingSystem() }, { it.name() }))
}

Expand All @@ -108,7 +155,7 @@ class FunctionAppCreateNewComponent(lifetime: Lifetime) :
* Note: Function Apps cannot use "Free" and "Shared" Pricing Tiers.
* Add Consumption plan for pricing on demand
*/
private fun updatePricingTiers(prices: List<PricingTier>) =
prices.filter { it != PricingTier.FREE_F1 && it != PricingTier.SHARED_D1 } +
FunctionAppPublishModel.consumptionPricingTier
private fun filterPricingTiers(prices: List<PricingTier>) =
(prices.filter { it != PricingTier.FREE_F1 && it != PricingTier.SHARED_D1 } +
FunctionAppPublishModel.dynamicPricingTier).distinct()
}
Loading

0 comments on commit 86c9d6f

Please sign in to comment.