Skip to content

Commit

Permalink
Merge pull request #44992 from mcruzdev/config-azure-functions
Browse files Browse the repository at this point in the history
Convert azure-functions to use @ConfigMapping
  • Loading branch information
gsmet authored Dec 9, 2024
2 parents fa716ef + 35c39a9 commit 754215b
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 103 deletions.
3 changes: 0 additions & 3 deletions extensions/azure-functions/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,6 @@
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-AlegacyConfigRoot=true</arg>
</compilerArgs>
</configuration>
</execution>
</executions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import java.util.Properties;

import org.apache.commons.lang3.StringUtils;
import org.jboss.logging.Logger;

import com.azure.core.management.AzureEnvironment;
import com.microsoft.azure.toolkit.lib.Azure;
Expand All @@ -31,185 +30,178 @@
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;

/**
* Azure Functions configuration.
* Most options supported and name similarly to azure-functions-maven-plugin config
*/
@ConfigRoot(phase = ConfigPhase.BUILD_TIME)
public class AzureFunctionsConfig {
@ConfigMapping(prefix = "quarkus.azure-functions")
public interface AzureFunctionsConfig {

/**
* App name for azure function project. This is required setting.
*
* Defaults to the base artifact name
*/
@ConfigItem
public Optional<String> appName;
Optional<String> appName();

/**
* Azure Resource Group for your Azure Functions
*/
@ConfigItem(defaultValue = "quarkus")
public String resourceGroup;
@WithDefault("quarkus")
String resourceGroup();

/**
* Specifies the region where your Azure Functions will be hosted; default value is westus.
* <a href=
* "https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-regions">Valid
* values</a>
*/
@ConfigItem(defaultValue = "westus")
public String region;
@WithDefault("westus")
String region();

/**
* Specifies whether to disable application insights for your function app
*/
@ConfigItem(defaultValue = "false")
public boolean disableAppInsights;
@WithDefault("false")
boolean disableAppInsights();

/**
* Specifies the instrumentation key of application insights which will bind to your function app
*/
@ConfigItem
public Optional<String> appInsightsKey;
Optional<String> appInsightsKey();

public RuntimeConfig runtime;
RuntimeConfig runtime();

public AuthConfig auth;
AuthConfig auth();

/**
* Specifies the name of the existing App Service Plan when you do not want to create a new one.
*/
@ConfigItem(defaultValue = "java-functions-app-service-plan")
public String appServicePlanName;
@WithDefault("java-functions-app-service-plan")
String appServicePlanName();

/**
* The app service plan resource group.
*/
@ConfigItem
public Optional<String> appServicePlanResourceGroup;
Optional<String> appServicePlanResourceGroup();

/**
* Azure subscription id. Required only if there are more than one subscription in your account
*/
@ConfigItem
public Optional<String> subscriptionId;
Optional<String> subscriptionId();

/**
* The pricing tier.
*/
@ConfigItem
public Optional<String> pricingTier;
Optional<String> pricingTier();

/**
* Port to run azure function in local runtime.
* Will default to quarkus.http.test-port or 8081
*/
@ConfigItem
public Optional<Integer> funcPort;
Optional<Integer> funcPort();

/**
* Config String for local debug
*/
@ConfigItem(defaultValue = "transport=dt_socket,server=y,suspend=n,address=5005")
public String localDebugConfig;
@WithDefault("transport=dt_socket,server=y,suspend=n,address=5005")
String localDebugConfig();

/**
* Specifies the application settings for your Azure Functions, which are defined in name-value pairs
*/
@ConfigItem
@ConfigDocMapKey("setting-name")
public Map<String, String> appSettings = Collections.emptyMap();
Map<String, String> appSettings = Collections.emptyMap();

@ConfigGroup
public static class RuntimeConfig {
interface RuntimeConfig {
/**
* Valid values are linux, windows, and docker
*/
@ConfigItem(defaultValue = "linux")
public String os;
@WithDefault("linux")
String os();

/**
* Valid values are 8, 11, and 17
*/
@ConfigItem(defaultValue = "11")
public String javaVersion;
@WithDefault("11")
String javaVersion();

/**
* URL of docker image if deploying via docker
*/
@ConfigItem
public Optional<String> image;
Optional<String> image();

/**
* If using docker, url of registry
*/
@ConfigItem
public Optional<String> registryUrl;
Optional<String> registryUrl();

}

public FunctionAppConfig toFunctionAppConfig(String subscriptionId, String appName) {
default FunctionAppConfig toFunctionAppConfig(String subscriptionId, String appName) {
Map<String, String> appSettings = this.appSettings;
if (appSettings.isEmpty()) {
appSettings = new HashMap<>();
appSettings.put("FUNCTIONS_EXTENSION_VERSION", "~4");
}
return (FunctionAppConfig) new FunctionAppConfig()
.disableAppInsights(disableAppInsights)
.appInsightsKey(appInsightsKey.orElse(null))
.appInsightsInstance(appInsightsKey.orElse(null))
.disableAppInsights(disableAppInsights())
.appInsightsKey(appInsightsKey().orElse(null))
.appInsightsInstance(appInsightsKey().orElse(null))
.subscriptionId(subscriptionId)
.resourceGroup(resourceGroup)
.resourceGroup(resourceGroup())
.appName(appName)
.servicePlanName(appServicePlanName)
.servicePlanResourceGroup(appServicePlanResourceGroup.orElse(null))
//.deploymentSlotName(getDeploymentSlotName())
//.deploymentSlotConfigurationSource(getDeploymentSlotConfigurationSource())
.servicePlanName(appServicePlanName())
.servicePlanResourceGroup(appServicePlanResourceGroup().orElse(null))
.pricingTier(getParsedPricingTier(subscriptionId))
.region(getParsedRegion())
.runtime(getRuntimeConfig(subscriptionId))
.appSettings(appSettings);
}

private PricingTier getParsedPricingTier(String subscriptionId) {
return Optional.ofNullable(this.pricingTier.orElse(null)).map(PricingTier::fromString)
return Optional.ofNullable(this.pricingTier().orElse(null)).map(PricingTier::fromString)
.orElseGet(() -> Optional.ofNullable(getServicePlan(subscriptionId)).map(AppServicePlan::getPricingTier)
.orElse(null));
}

private AppServicePlan getServicePlan(String subscriptionId) {
final String servicePlan = this.appServicePlanName;
final String servicePlanGroup = StringUtils.firstNonBlank(this.appServicePlanResourceGroup.orElse(null),
this.resourceGroup);
final String servicePlan = this.appServicePlanName();
final String servicePlanGroup = StringUtils.firstNonBlank(this.appServicePlanResourceGroup().orElse(null),
this.resourceGroup());
return StringUtils.isAnyBlank(subscriptionId, servicePlan, servicePlanGroup) ? null
: Azure.az(AzureAppService.class).plans(subscriptionId).get(servicePlan, servicePlanGroup);
}

private com.microsoft.azure.toolkit.lib.appservice.config.RuntimeConfig getRuntimeConfig(String subscriptionId) {
final RuntimeConfig runtime = this.runtime;
final RuntimeConfig runtime = this.runtime();
if (runtime == null) {
return null;
}
final OperatingSystem os = Optional.ofNullable(runtime.os).map(OperatingSystem::fromString)
final OperatingSystem os = Optional.ofNullable(runtime.os()).map(OperatingSystem::fromString)
.orElseGet(
() -> Optional.ofNullable(getServicePlan(subscriptionId)).map(AppServicePlan::getOperatingSystem)
.orElse(null));
final JavaVersion javaVersion = Optional.ofNullable(runtime.javaVersion).map(JavaVersion::fromString).orElse(null);
final JavaVersion javaVersion = Optional.ofNullable(runtime.javaVersion()).map(JavaVersion::fromString).orElse(null);
final com.microsoft.azure.toolkit.lib.appservice.config.RuntimeConfig result = new com.microsoft.azure.toolkit.lib.appservice.config.RuntimeConfig()
.os(os)
.javaVersion(javaVersion).webContainer(WebContainer.JAVA_OFF)
.image(runtime.image.orElse(null)).registryUrl(runtime.registryUrl.orElse(null));
.image(runtime.image().orElse(null)).registryUrl(runtime.registryUrl().orElse(null));
return result;
}

private Region getParsedRegion() {
return Optional.ofNullable(region).map(Region::fromName).orElse(null);
return Optional.ofNullable(region()).map(Region::fromName).orElse(null);
}

@ConfigGroup
public static class AuthConfig {
interface AuthConfig {

/**
* Description of each type can be found
Expand All @@ -234,28 +226,23 @@ public static class AuthConfig {
*
* Defaults to "azure_cli" for authentication
*/
@ConfigItem(defaultValue = "azure_cli")
public String type;
@WithDefault("azure_cli")
String type();

/**
* Filesystem path to properties file if using <i>file</i> type
*/
@ConfigItem
public Optional<String> path;
Optional<String> path();

/**
* Client or App Id required if using <i>managed_identity</i> type
*/
@ConfigItem
public Optional<String> client;
Optional<String> client();

/**
* Tenant Id required if using <i>oauth2</i> or <i>device_code</i> type
* Tenant ID required if using <i>oauth2</i> or <i>device_code</i> type
*/
@ConfigItem
public Optional<String> tenant;

private static final Logger log = Logger.getLogger(AzureFunctionsConfig.class);
Optional<String> tenant();

private static String findValue(Properties props, String key) {
if (props.contains(key))
Expand Down Expand Up @@ -301,15 +288,15 @@ private static AuthConfiguration fromFile(Optional<String> path) {
}
}

public AuthConfiguration toAuthConfiguration() {
default AuthConfiguration toAuthConfiguration() {
try {
if (this.type.equalsIgnoreCase("file")) {
return fromFile(this.path);
if (this.type().equalsIgnoreCase("file")) {
return fromFile(this.path());
}
final AuthType type = AuthType.parseAuthType(this.type);
final AuthType type = AuthType.parseAuthType(this.type());
final AuthConfiguration authConfiguration = new AuthConfiguration(type);
authConfiguration.setClient(client.orElse(null));
authConfiguration.setTenant(tenant.orElse(null));
authConfiguration.setClient(client().orElse(null));
authConfiguration.setTenant(tenant().orElse(null));
authConfiguration.setEnvironment(AzureEnvironmentUtils.azureEnvironmentToString(AzureEnvironment.AZURE));
return authConfiguration;
} catch (InvalidConfigurationException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,22 +167,22 @@ protected void validateParameters(AzureFunctionsConfig config, String appName) t
throw new BuildException(INVALID_APP_NAME);
}
// resource group
if (StringUtils.isBlank(config.resourceGroup)) {
if (StringUtils.isBlank(config.resourceGroup())) {
throw new BuildException(EMPTY_RESOURCE_GROUP);
}
if (config.resourceGroup.endsWith(".") || !config.resourceGroup.matches(RESOURCE_GROUP_PATTERN)) {
if (config.resourceGroup().endsWith(".") || !config.resourceGroup().matches(RESOURCE_GROUP_PATTERN)) {
throw new BuildException(INVALID_RESOURCE_GROUP_NAME);
}
// asp name & resource group
if (StringUtils.isNotEmpty(config.appServicePlanName)
&& !config.appServicePlanName.matches(APP_SERVICE_PLAN_NAME_PATTERN)) {
if (StringUtils.isNotEmpty(config.appServicePlanName())
&& !config.appServicePlanName().matches(APP_SERVICE_PLAN_NAME_PATTERN)) {
throw new BuildException(String.format(INVALID_SERVICE_PLAN_NAME, APP_SERVICE_PLAN_NAME_PATTERN));
}
if (config.appServicePlanResourceGroup.isPresent()
&& StringUtils.isNotEmpty(config.appServicePlanResourceGroup.orElse(null))
if (config.appServicePlanResourceGroup().isPresent()
&& StringUtils.isNotEmpty(config.appServicePlanResourceGroup().orElse(null))
&&
(config.appServicePlanResourceGroup.orElse(null).endsWith(".")
|| !config.appServicePlanResourceGroup.orElse(null).matches(RESOURCE_GROUP_PATTERN))) {
(config.appServicePlanResourceGroup().orElse(null).endsWith(".")
|| !config.appServicePlanResourceGroup().orElse(null).matches(RESOURCE_GROUP_PATTERN))) {
throw new BuildException(INVALID_SERVICE_PLAN_RESOURCE_GROUP_NAME);
}
// slot name
Expand All @@ -196,26 +196,26 @@ protected void validateParameters(AzureFunctionsConfig config, String appName) t
*
*/
// region
if (StringUtils.isNotEmpty(config.region) && Region.fromName(config.region).isExpandedValue()) {
log.warn(String.format(EXPANDABLE_REGION_WARNING, config.region));
if (StringUtils.isNotEmpty(config.region()) && Region.fromName(config.region()).isExpandedValue()) {
log.warn(String.format(EXPANDABLE_REGION_WARNING, config.region()));
}
// os
if (StringUtils.isNotEmpty(config.runtime.os) && OperatingSystem.fromString(config.runtime.os) == null) {
if (StringUtils.isNotEmpty(config.runtime().os()) && OperatingSystem.fromString(config.runtime().os()) == null) {
throw new BuildException(INVALID_OS);
}
// java version
if (StringUtils.isNotEmpty(config.runtime.javaVersion)
&& JavaVersion.fromString(config.runtime.javaVersion).isExpandedValue()) {
log.warn(String.format(EXPANDABLE_JAVA_VERSION_WARNING, config.runtime.javaVersion));
if (StringUtils.isNotEmpty(config.runtime().javaVersion())
&& JavaVersion.fromString(config.runtime().javaVersion()).isExpandedValue()) {
log.warn(String.format(EXPANDABLE_JAVA_VERSION_WARNING, config.runtime().javaVersion()));
}
// pricing tier
if (config.pricingTier.isPresent() && StringUtils.isNotEmpty(config.pricingTier.orElse(null))
&& PricingTier.fromString(config.pricingTier.orElse(null)).isExpandedValue()) {
log.warn(String.format(EXPANDABLE_PRICING_TIER_WARNING, config.pricingTier.orElse(null)));
if (config.pricingTier().isPresent() && StringUtils.isNotEmpty(config.pricingTier().orElse(null))
&& PricingTier.fromString(config.pricingTier().orElse(null)).isExpandedValue()) {
log.warn(String.format(EXPANDABLE_PRICING_TIER_WARNING, config.pricingTier().orElse(null)));
}
// docker image
if (OperatingSystem.fromString(config.runtime.os) == OperatingSystem.DOCKER
&& StringUtils.isEmpty(config.runtime.image.orElse(null))) {
if (OperatingSystem.fromString(config.runtime().os()) == OperatingSystem.DOCKER
&& StringUtils.isEmpty(config.runtime().image().orElse(null))) {
throw new BuildException(EMPTY_IMAGE_NAME);
}
}
Expand All @@ -226,9 +226,9 @@ protected void validateParameters(AzureFunctionsConfig config, String appName) t

protected AzureAppService initAzureAppServiceClient(AzureFunctionsConfig config) throws BuildException {
if (appServiceClient == null) {
final Account account = loginAzure(config.auth);
final Account account = loginAzure(config.auth());
final List<Subscription> subscriptions = account.getSubscriptions();
final String targetSubscriptionId = getTargetSubscriptionId(config.subscriptionId.orElse(null), subscriptions,
final String targetSubscriptionId = getTargetSubscriptionId(config.subscriptionId().orElse(null), subscriptions,
account.getSelectedSubscriptions());
checkSubscription(subscriptions, targetSubscriptionId);
com.microsoft.azure.toolkit.lib.Azure.az(AzureAccount.class).account()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ FeatureBuildItem feature() {

@BuildStep
AzureFunctionsAppNameBuildItem appName(OutputTargetBuildItem output, AzureFunctionsConfig functionsConfig) {
String appName = functionsConfig.appName.orElse(output.getBaseName());
String appName = functionsConfig.appName().orElse(output.getBaseName());
return new AzureFunctionsAppNameBuildItem(appName);
}

Expand Down
Loading

0 comments on commit 754215b

Please sign in to comment.