From 47f13b04665d464f69c7ce70925093d1a25c91df Mon Sep 17 00:00:00 2001 From: Ankit Kumar Date: Fri, 20 Sep 2019 19:40:45 -0700 Subject: [PATCH] Add ability to grab the FRAMEWORK_VERSION for function apps --- .../Generator/SiteBuilderFactory.cs | 5 ++ Kudu.Core/Deployment/Oryx/BuildFlags.cs | 6 +- .../Oryx/FunctionAppOryxArguments.cs | 60 +++++++++++++++++-- .../Oryx/FunctionAppSupportedWorkerRuntime.cs | 10 +--- .../Deployment/Oryx/OryxBuildConstants.cs | 4 +- .../Oryx/OryxArgumentsFunctionAppTests.cs | 13 ++++ 6 files changed, 79 insertions(+), 19 deletions(-) diff --git a/Kudu.Core/Deployment/Generator/SiteBuilderFactory.cs b/Kudu.Core/Deployment/Generator/SiteBuilderFactory.cs index e7404044..644a55da 100644 --- a/Kudu.Core/Deployment/Generator/SiteBuilderFactory.cs +++ b/Kudu.Core/Deployment/Generator/SiteBuilderFactory.cs @@ -64,6 +64,7 @@ public ISiteBuilder CreateBuilder(ITracer tracer, ILogger logger, IDeploymentSet return new BasicBuilder(_environment, settings, _propertyProvider, repositoryRoot, projectPath); } + // If ENABLE_ORYX_BUILD is not set, for function app, we assume it on by default string enableOryxBuild = System.Environment.GetEnvironmentVariable("ENABLE_ORYX_BUILD"); if (!string.IsNullOrEmpty(enableOryxBuild)) { @@ -72,6 +73,10 @@ public ISiteBuilder CreateBuilder(ITracer tracer, ILogger logger, IDeploymentSet return new OryxBuilder(_environment, settings, _propertyProvider, repositoryRoot); } } + else if (FunctionAppHelper.LooksLikeFunctionApp()) + { + return new OryxBuilder(_environment, settings, _propertyProvider, repositoryRoot); + } if (!String.IsNullOrEmpty(targetProjectPath)) { diff --git a/Kudu.Core/Deployment/Oryx/BuildFlags.cs b/Kudu.Core/Deployment/Oryx/BuildFlags.cs index 7b278476..60776e4c 100644 --- a/Kudu.Core/Deployment/Oryx/BuildFlags.cs +++ b/Kudu.Core/Deployment/Oryx/BuildFlags.cs @@ -15,11 +15,11 @@ public enum BuildOptimizationsFlags public class BuildFlagsHelper { - public static BuildOptimizationsFlags Parse(string value) + public static BuildOptimizationsFlags Parse(string value, BuildOptimizationsFlags defaultVal = BuildOptimizationsFlags.None) { if (string.IsNullOrEmpty(value)) { - return BuildOptimizationsFlags.None; + return defaultVal; } try @@ -29,7 +29,7 @@ public static BuildOptimizationsFlags Parse(string value) } catch (Exception) { - return BuildOptimizationsFlags.None; + return defaultVal; } } } diff --git a/Kudu.Core/Deployment/Oryx/FunctionAppOryxArguments.cs b/Kudu.Core/Deployment/Oryx/FunctionAppOryxArguments.cs index edae7556..ac41344e 100644 --- a/Kudu.Core/Deployment/Oryx/FunctionAppOryxArguments.cs +++ b/Kudu.Core/Deployment/Oryx/FunctionAppOryxArguments.cs @@ -1,6 +1,8 @@ using Kudu.Core.Infrastructure; +using System; using System.IO; using System.Text; +using System.Text.RegularExpressions; namespace Kudu.Core.Deployment.Oryx { @@ -19,7 +21,7 @@ public FunctionAppOryxArguments() FunctionsWorkerRuntime = ResolveWorkerRuntime(); RunOryxBuild = FunctionsWorkerRuntime != WorkerRuntime.None; var buildFlags = GetEnvironmentVariableOrNull(OryxBuildConstants.OryxEnvVars.BuildFlagsSetting); - Flags = BuildFlagsHelper.Parse(buildFlags); + Flags = BuildFlagsHelper.Parse(buildFlags, defaultVal: BuildOptimizationsFlags.UseExpressBuild); SkipKuduSync = Flags == BuildOptimizationsFlags.UseExpressBuild; } @@ -70,10 +72,6 @@ protected void AddLanguage(StringBuilder args, WorkerRuntime workerRuntime) case WorkerRuntime.Python: OryxArgumentsHelper.AddLanguage(args, "python"); break; - - case WorkerRuntime.PHP: - OryxArgumentsHelper.AddLanguage(args, "php"); - break; } } @@ -121,9 +119,61 @@ private WorkerRuntime ResolveWorkerRuntime() private string ResolveWorkerRuntimeVersion(WorkerRuntime workerRuntime) { + var framework = GetEnvironmentVariableOrNull(OryxBuildConstants.OryxEnvVars.FrameworkSetting); + var frameworkVersion = GetEnvironmentVariableOrNull(OryxBuildConstants.OryxEnvVars.FrameworkVersionSetting); + + // If either of them is not set we are not in a position to determine the version, and should let the caller + // switch to default + if (string.IsNullOrEmpty(framework) || string.IsNullOrEmpty(frameworkVersion)) + { + return FunctionAppSupportedWorkerRuntime.GetDefaultLanguageVersion(workerRuntime); + } + + // If it's set to DOCKER, it could be a) a function app image b) a custom image + // For custom image, there's no point doing a build, so we just default them (the parser won't work). + // if it's indeed a function app image, we look for the right tag that tells us about the version. + // + // Or if it's set to a supported worker runtime that was inferred, we assume that the framework version + // that is set is the correct version requested. + if (framework.Equals("DOCKER", StringComparison.OrdinalIgnoreCase)) + { + var parsedVersion = ParseRuntimeVersionFromImage(frameworkVersion); + if (!string.IsNullOrEmpty(parsedVersion)) + { + return parsedVersion; + } + } + else if (framework.Equals(workerRuntime.ToString(), StringComparison.OrdinalIgnoreCase)) + { + return frameworkVersion; + } + return FunctionAppSupportedWorkerRuntime.GetDefaultLanguageVersion(workerRuntime); } + public static string ParseRuntimeVersionFromImage(string imageName) + { + // The image name would be in this format -- 'mcr.microsoft.com/azure-functions/python:2.0-python3.6-appservice' + // If we get any parsing issues, we return null here. + try + { + var imageTag = imageName.Substring(imageName.LastIndexOf(':') + 1); + var versionRegex = new Regex(@"^[0-9]+\.[0-9]+\-[A-Za-z]+([0-9].*)\-appservice$"); + var versionMatch = versionRegex.Match(imageTag); + var version = versionMatch?.Groups[1]?.Value; + if (!string.IsNullOrEmpty(version)) + { + return version; + } + return null; + } + catch (Exception) + { + // TODO: Log at ETW event later + return null; + } + } + private string GetEnvironmentVariableOrNull(string environmentVarName) { var environmentVarValue = System.Environment.GetEnvironmentVariable(environmentVarName); diff --git a/Kudu.Core/Deployment/Oryx/FunctionAppSupportedWorkerRuntime.cs b/Kudu.Core/Deployment/Oryx/FunctionAppSupportedWorkerRuntime.cs index 513a4b5b..9375cfe8 100644 --- a/Kudu.Core/Deployment/Oryx/FunctionAppSupportedWorkerRuntime.cs +++ b/Kudu.Core/Deployment/Oryx/FunctionAppSupportedWorkerRuntime.cs @@ -7,8 +7,7 @@ public enum WorkerRuntime None, Node, Python, - DotNet, - PHP + DotNet } public class FunctionAppSupportedWorkerRuntime @@ -31,10 +30,6 @@ public static WorkerRuntime ParseWorkerRuntime(string value) { return WorkerRuntime.DotNet; } - else if (value.StartsWith("PHP", StringComparison.OrdinalIgnoreCase)) - { - return WorkerRuntime.PHP; - } return WorkerRuntime.None; } @@ -52,9 +47,6 @@ public static string GetDefaultLanguageVersion(WorkerRuntime workerRuntime) case WorkerRuntime.Python: return OryxBuildConstants.FunctionAppWorkerRuntimeDefaults.Python; - case WorkerRuntime.PHP: - return OryxBuildConstants.FunctionAppWorkerRuntimeDefaults.PHP; - default: return ""; } diff --git a/Kudu.Core/Deployment/Oryx/OryxBuildConstants.cs b/Kudu.Core/Deployment/Oryx/OryxBuildConstants.cs index 0ca96396..bbc3fc86 100644 --- a/Kudu.Core/Deployment/Oryx/OryxBuildConstants.cs +++ b/Kudu.Core/Deployment/Oryx/OryxBuildConstants.cs @@ -1,4 +1,5 @@ -using System.IO; +using System.Collections.Generic; +using System.IO; namespace Kudu.Core.Deployment.Oryx { @@ -23,7 +24,6 @@ internal static class FunctionAppWorkerRuntimeDefaults public static readonly string Node = "8.15"; public static readonly string Python = "3.6"; public static readonly string Dotnet = "2.2"; - public static readonly string PHP = "7.3"; } internal static class FunctionAppBuildSettings diff --git a/Kudu.Tests/Core/Deployment/Oryx/OryxArgumentsFunctionAppTests.cs b/Kudu.Tests/Core/Deployment/Oryx/OryxArgumentsFunctionAppTests.cs index 53944235..3ed949a0 100644 --- a/Kudu.Tests/Core/Deployment/Oryx/OryxArgumentsFunctionAppTests.cs +++ b/Kudu.Tests/Core/Deployment/Oryx/OryxArgumentsFunctionAppTests.cs @@ -106,5 +106,18 @@ public void SkipKuduSyncOnExpressBuildTest() command); } } + + [Theory] + [InlineData("mcr.microsoft.com/azure-functions/python:2.0-python3.7-appservice", "3.7")] + [InlineData("mcr.microsoft.com/azure-functions/python:2.0-python3.6-appservice", "3.6")] + [InlineData("mcr.microsoft.com/azure-functions/python:2.0-node8-appservice", "8")] + [InlineData("mcr.microsoft.com/azure-functions/python:2.0-node10-appservice", "10")] + [InlineData("mcr.microsoft.com/azure-functions/python:2.0-dotnet-appservice", null)] + [InlineData("mcr.microsoft.com/azure-functions/python:2.0-dotnet2.0-appservice", "2.0")] + [InlineData("mcr.microsoft.com/azure-functions/python:2.0-dotnet3.0-appservice", "3.0")] + public void TestVersionFromImage(string imageName, string version) + { + Assert.Equal(version, FunctionAppOryxArguments.ParseRuntimeVersionFromImage(imageName)); + } } }