Skip to content

Commit f85b71a

Browse files
aishwaryabhkshyju
authored andcommitted
Core Tools OOP Host (#3802)
* this doesn't work * this finally works now * default should be oop host * added edge cases * fixing formatting * using determineTargetFramework * saving tests * adding copy step for OOP * fixing the webhost reference * removing test * setting host version in ps script * update to validate worker versions script * update versions for worker packages * adding ToString * validating worker version * validate worker versions * updating csproj to compile * addressing pr feedback * updating build steps * adding build step * fixing build step * trying to get this working * reverrting target runtimes * updating tests * adding dotnet info step * adding changes * removing extra test * trying to specify architecture * modifying tests to see if they work * narrowing down to tests that are failing * trying to see if it works with nobuild flag * addressing pr feedback * updating tests with latest logging * addressing comments and marking flaky tests * updating so that we are only using net8 framework * pushing change for branch build * adding single quotes * reverting quotes * adding code mirror fiile * updating build step * updatinng build steps * updating build step * adding step for dotnet publish * set inprochost compilation system to diff value and skip flaky test * updating public build pipeline to trigger * adding extra changes for pipeline * public build yml * updating official build * readd net8 build artifact step * readding space back * addressing initial comments * adding explicit openTelemetry dlls * simplifying logic of startHostAction * addressing PR feedback * start tests * changing some of the tests back * reverting test back to normal * fixing spacing for csproj * addressing PR feedback * adding extra variable * adding logic for edge case scenarios * added edge cases tests * removing extra line in node * addressing comments * moving validate host runtime to its own method * forgot to add return statement
1 parent 935dbbd commit f85b71a

File tree

16 files changed

+552
-75
lines changed

16 files changed

+552
-75
lines changed

build.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ if ($env:IntegrationBuildNumber)
2323
throw $errorMessage
2424
}
2525

26-
$buildCommand = { dotnet run --integrationTests }
26+
$buildCommand = { dotnet run --integrationTests --skipArtifactGeneration}
2727
}
2828
else
2929
{
30-
$buildCommand = { dotnet run --ci }
30+
$buildCommand = { dotnet run --ci --skipArtifactGeneration}
3131
}
3232

3333
Write-Host "Running $buildCommand"

build/BuildSteps.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ public static void DotnetPack()
102102
Shell.Run("dotnet", $"pack {Settings.SrcProjectPath} " +
103103
$"/p:BuildNumber=\"{Settings.BuildNumber}\" " +
104104
$"/p:NoWorkers=\"true\" " +
105-
$"/p:TargetFramework=net6.0 " + // without TargetFramework, the generated nuspec has incorrect path for the copy files operation.
105+
$"/p:TargetFramework=net8.0 " + // without TargetFramework, the generated nuspec has incorrect path for the copy files operation.
106106
$"/p:CommitHash=\"{Settings.CommitId}\" " +
107107
(string.IsNullOrEmpty(Settings.IntegrationBuildNumber) ? string.Empty : $"/p:IntegrationBuildNumber=\"{Settings.IntegrationBuildNumber}\" ") +
108108
$"-o {outputPath} -c Release --no-build");
@@ -116,8 +116,7 @@ public static void DotnetPublishForZips()
116116
var outputPath = Path.Combine(Settings.OutputDir, runtime);
117117
var rid = GetRuntimeId(runtime);
118118

119-
ExecuteDotnetPublish(outputPath, rid, "net6.0", skipLaunchingNet8ChildProcess: isMinVersion);
120-
119+
ExecuteDotnetPublish(outputPath, rid, "net8.0", skipLaunchingNet8ChildProcess: isMinVersion);
121120
if (isMinVersion)
122121
{
123122
RemoveLanguageWorkers(outputPath);
@@ -342,7 +341,7 @@ public static void Test()
342341

343342
Environment.SetEnvironmentVariable("DURABLE_FUNCTION_PATH", Settings.DurableFolder);
344343

345-
Shell.Run("dotnet", $"test {Settings.TestProjectFile} -f net6.0 --logger trx");
344+
Shell.Run("dotnet", $"test {Settings.TestProjectFile} -f net8.0 --logger trx");
346345
}
347346

348347
public static void CopyBinariesToSign()
@@ -643,10 +642,10 @@ public static void DotnetPublishForNupkg()
643642
Shell.Run("dotnet", $"publish {Settings.ProjectFile} " +
644643
$"/p:BuildNumber=\"{Settings.BuildNumber}\" " +
645644
$"/p:NoWorkers=\"true\" " +
646-
$"/p:TargetFramework=net6.0 " +
645+
$"/p:TargetFramework=net8.0 " +
647646
$"/p:CommitHash=\"{Settings.CommitId}\" " +
648647
(string.IsNullOrEmpty(Settings.IntegrationBuildNumber) ? string.Empty : $"/p:IntegrationBuildNumber=\"{Settings.IntegrationBuildNumber}\" ") +
649-
$"-c Release -f net6.0");
648+
$"-c Release -f net8.0");
650649
}
651650

652651
public static void GenerateSBOMManifestForNupkg()

build/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ static void Main(string[] args)
2929
.Then(TestPreSignedArtifacts, skip: !args.Contains("--ci"))
3030
.Then(CopyBinariesToSign, skip: !args.Contains("--ci"))
3131
.Then(Test)
32-
.Then(Zip)
32+
.Then(Zip, skip: args.Contains("--skipArtifactGeneration"))
3333
.Then(DotnetPublishForNupkg)
3434
.Then(DotnetPack)
3535
.Then(CreateIntegrationTestsBuildManifest, skip: !args.Contains("--integrationTests"))
36-
.Then(UploadToStorage, skip: !args.Contains("--ci"))
36+
.Then(UploadToStorage, skip: !args.Contains("--ci") || args.Contains("--skipArtifactGeneration"))
3737
.Run();
3838
}
3939
}

build/Settings.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,16 @@ public class SignInfo
341341
"Microsoft.OData.Edm.dll",
342342
"Microsoft.Spatial.dll",
343343
"Mono.Posix.NETStandard.dll",
344+
"OpenTelemetry.Api.dll",
345+
"OpenTelemetry.Api.ProviderBuilderExtensions.dll",
346+
"OpenTelemetry.dll",
347+
"OpenTelemetry.Exporter.Console.dll",
348+
"OpenTelemetry.Exporter.OpenTelemetryProtocol.dll",
349+
"OpenTelemetry.Extensions.Hosting.dll",
350+
"OpenTelemetry.Instrumentation.AspNetCore.dll",
351+
"OpenTelemetry.Instrumentation.Http.dll",
352+
"OpenTelemetry.PersistentStorage.Abstractions.dll",
353+
"OpenTelemetry.PersistentStorage.FileSystem.dll",
344354
Path.Combine("tools", "python", "packapp", "distlib")
345355
};
346356
}

code-mirror.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ trigger:
88
- release_4.0
99
- release_3.0
1010
- release_4.0_hotfix
11+
- feature/*
1112

1213
resources:
1314
repositories:

eng/ci/official-build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ trigger:
1515
include:
1616
- v4.x
1717
- release_4.0
18+
- feature/*
1819

1920
resources:
2021
repositories:

eng/ci/public-build.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ pr:
1313
include:
1414
- v4.x
1515
- release_4.0
16+
- feature/*
1617

1718
trigger:
1819
batch: true
1920
branches:
2021
include:
2122
- v4.x
2223
- release_4.0
24+
- feature/*
2325

2426
resources:
2527
repositories:

pipelineUtilities.psm1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ function Install-DotnetVersion($Version,$Channel) {
124124
if ($IsWindows) {
125125
& .\$installScript -InstallDir "$env:ProgramFiles/dotnet" -Channel $Channel -Version $Version
126126
# Installing .NET into x86 directory since the E2E App runs the tests on x86 and looks for the specified framework there
127-
& .\$installScript -InstallDir "$env:ProgramFiles (x86)/dotnet" -Channel $Channel -Version $Version
127+
& .\$installScript -InstallDir "$env:ProgramFiles (x86)/dotnet" -Channel $Channel -Version $Version -Architecture x86
128128
} else {
129129
bash ./$installScript --install-dir /usr/share/dotnet -c $Channel -v $Version
130130
}

src/Azure.Functions.Cli/Actions/HostActions/StartHostAction.cs

Lines changed: 125 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using System.Net;
88
using System.Runtime.InteropServices;
99
using System.Security.Cryptography.X509Certificates;
10-
using System.Text.RegularExpressions;
1110
using System.Threading.Tasks;
1211
using Azure.Functions.Cli.Common;
1312
using Azure.Functions.Cli.Diagnostics;
@@ -24,6 +23,7 @@
2423
using Microsoft.Azure.WebJobs.Script.Configuration;
2524
using Microsoft.Azure.WebJobs.Script.Description;
2625
using Microsoft.Azure.WebJobs.Script.WebHost;
26+
using Microsoft.Azure.WebJobs.Script.Workers;
2727
using Microsoft.Extensions.Configuration;
2828
using Microsoft.Extensions.DependencyInjection;
2929
using Microsoft.Extensions.Logging;
@@ -40,10 +40,6 @@ internal class StartHostAction : BaseAction
4040
{
4141
private const int DefaultPort = 7071;
4242
private const int DefaultTimeout = 20;
43-
private const string Net6FrameworkDescriptionPrefix = ".NET 6.0";
44-
private const string WindowsExecutableName = "func.exe";
45-
private const string LinuxExecutableName = "func";
46-
private const string InProc8DirectoryName = "in-proc8";
4743
private readonly ISecretsManager _secretsManager;
4844
private readonly IProcessManager _processManager;
4945
private IConfigurationRoot _hostJsonConfig;
@@ -81,6 +77,8 @@ internal class StartHostAction : BaseAction
8177

8278
public string JsonOutputFile { get; set; }
8379

80+
public string? HostRuntime { get; set; }
81+
8482
public StartHostAction(ISecretsManager secretsManager, IProcessManager processManager)
8583
{
8684
_secretsManager = secretsManager;
@@ -176,6 +174,11 @@ public override ICommandLineParserResult ParseArgs(string[] args)
176174
.WithDescription("If provided, a path to the file that will be used to write the output when using --enable-json-output.")
177175
.Callback(jsonOutputFile => JsonOutputFile = jsonOutputFile);
178176

177+
Parser
178+
.Setup<string>("runtime")
179+
.WithDescription($"If provided, determines which version of the host to start. Allowed values are '{DotnetConstants.InProc6HostRuntime}', '{DotnetConstants.InProc8HostRuntime}', and 'default' (which runs the out-of-process host).")
180+
.Callback(startHostFromRuntime => HostRuntime = startHostFromRuntime);
181+
179182
var parserResult = base.ParseArgs(args);
180183
bool verboseLoggingArgExists = parserResult.UnMatchedOptions.Any(o => o.LongName.Equals("verbose", StringComparison.OrdinalIgnoreCase));
181184
// Input args do not contain --verbose flag
@@ -418,15 +421,14 @@ private async Task<JObject> GetLocalSettingsJsonAsJObjectAsync()
418421
public override async Task RunAsync()
419422
{
420423
await PreRunConditions();
421-
422-
var isCurrentProcessNet6Build = RuntimeInformation.FrameworkDescription.Contains(Net6FrameworkDescriptionPrefix);
423-
if (isCurrentProcessNet6Build && ShouldLaunchInProcNet8AsChildProcess() && await IsInProcNet8Enabled())
424+
var isVerbose = VerboseLogging.HasValue && VerboseLogging.Value;
425+
426+
// Return if we have already started a child process
427+
if (await ShouldExitAfterDeterminingHostRuntime(isVerbose))
424428
{
425-
await StartInProc8AsChildProcessAsync();
426429
return;
427430
}
428431

429-
var isVerbose = VerboseLogging.HasValue && VerboseLogging.Value;
430432
if (isVerbose || EnvironmentHelper.GetEnvironmentVariableAsBool(Constants.DisplayLogo))
431433
{
432434
Utilities.PrintLogo();
@@ -480,31 +482,128 @@ public override async Task RunAsync()
480482
await runTask;
481483
}
482484

483-
private static string GetInProcNet8ExecutablePath()
485+
private async Task<bool> ShouldExitAfterDeterminingHostRuntime(bool isVerbose)
486+
{
487+
var isInProc = GlobalCoreToolsSettings.CurrentWorkerRuntime == WorkerRuntime.dotnet;
488+
489+
// If --runtime param is set, handle runtime param logic; otherwise we infer the host to launch on startup
490+
if (HostRuntime is not null)
491+
{
492+
// Validate host runtime passed in
493+
await ValidateHostRuntime(isInProc, isVerbose);
494+
495+
if (!string.Equals(HostRuntime, "default", StringComparison.OrdinalIgnoreCase))
496+
{
497+
var isNet8InProcSpecified = string.Equals(HostRuntime, DotnetConstants.InProc8HostRuntime, StringComparison.OrdinalIgnoreCase);
498+
StartHostAsChildProcess(isNet8InProcSpecified);
499+
return true;
500+
}
501+
}
502+
else if (isInProc)
503+
{
504+
// Infer host runtime by checking if .NET 8 is enabled
505+
var shouldNet8InProcBeLaunched = await IsInProcNet8Enabled();
506+
507+
if (shouldNet8InProcBeLaunched)
508+
{
509+
PrintVerboseForHostSelection(isVerbose, DotnetConstants.InProc8HostRuntime);
510+
}
511+
// Otherwise start .NET 6 child process since we are running an inproc app
512+
else
513+
{
514+
PrintVerboseForHostSelection(isVerbose, DotnetConstants.InProc6HostRuntime);
515+
}
516+
517+
StartHostAsChildProcess(shouldNet8InProcBeLaunched);
518+
return true;
519+
520+
}
521+
// If the host runtime parameter is not set and the app is not in-proc, we should default to out-of-process host
522+
PrintVerboseForHostSelection(isVerbose, "out-of-process");
523+
return false;
524+
}
525+
526+
private async Task ValidateHostRuntime(bool isInProc, bool isVerbose)
527+
{
528+
var isOutOfProc = GlobalCoreToolsSettings.CurrentWorkerRuntime == WorkerRuntime.dotnetIsolated;
529+
var isInProc6 = string.Equals(HostRuntime, DotnetConstants.InProc6HostRuntime, StringComparison.OrdinalIgnoreCase);
530+
var isInProc8 = string.Equals(HostRuntime, DotnetConstants.InProc8HostRuntime, StringComparison.OrdinalIgnoreCase);
531+
532+
// If we are running an .NET isolated app and the user specifies inproc6 or inproc8, throw an error
533+
if (isOutOfProc && (isInProc8 || isInProc6))
534+
{
535+
throw new CliException($"The runtime host value passed in, {HostRuntime}, is not a valid host version for your project. The host runtime is only valid for the worker runtime {WorkerRuntime.dotnet}");
536+
}
537+
// If we are not running a .NET app and the user specifies inproc6 or inproc8, throw an error
538+
if (!isOutOfProc && !isInProc && (isInProc8 || isInProc6))
539+
{
540+
throw new CliException($"The runtime host value passed in, {HostRuntime}, is not a valid host version for your project. The runtime is only valid for {WorkerRuntime.dotnetIsolated} and {WorkerRuntime.dotnet}");
541+
}
542+
543+
if (string.Equals(HostRuntime, "default", StringComparison.OrdinalIgnoreCase))
544+
{
545+
if (isInProc)
546+
{
547+
throw new CliException($"The runtime host value passed in, default, is not a valid host version for your project. For the default host runtime, the worker runtime must be set to {WorkerRuntime.dotnetIsolated}.");
548+
}
549+
PrintVerboseForHostSelection(isVerbose, "out-of-process");
550+
return;
551+
}
552+
else if (isInProc8)
553+
{
554+
if (!await IsInProcNet8Enabled())
555+
{
556+
throw new CliException($"The runtime host value passed in, {DotnetConstants.InProc8HostRuntime}, is not a valid host version for your project. For the {DotnetConstants.InProc8HostRuntime} runtime, the {Constants.FunctionsInProcNet8Enabled} variable must be set while running a .NET 8 in-proc app.");
557+
}
558+
PrintVerboseForHostSelection(isVerbose, DotnetConstants.InProc8HostRuntime);
559+
return;
560+
}
561+
else if (isInProc6)
562+
{
563+
if (await IsInProcNet8Enabled())
564+
{
565+
throw new CliException($"The runtime host value passed in, {DotnetConstants.InProc6HostRuntime}, is not a valid host version for your project. For the {DotnetConstants.InProc6HostRuntime} runtime, the {Constants.FunctionsInProcNet8Enabled} variable must not be set while running a .NET 6 in-proc app.");
566+
}
567+
PrintVerboseForHostSelection(isVerbose, DotnetConstants.InProc6HostRuntime);
568+
return;
569+
}
570+
// Throw an exception if HostRuntime is set to none of the expected values
571+
throw new CliException($"Invalid host runtime '{HostRuntime}'. Valid values are 'default', '{DotnetConstants.InProc8HostRuntime}', '{DotnetConstants.InProc6HostRuntime}'.");
572+
}
573+
574+
private void PrintVerboseForHostSelection(bool isVerbose, string hostRuntime)
575+
{
576+
if (isVerbose)
577+
{
578+
ColoredConsole.WriteLine(VerboseColor($"Selected {hostRuntime} host."));
579+
}
580+
}
581+
582+
private static string GetInProcExecutablePath(bool isNet8)
484583
{
485584
var funcExecutableDirectory = Path.GetDirectoryName(typeof(StartHostAction).Assembly.Location)!;
486-
var executableName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? WindowsExecutableName : LinuxExecutableName;
585+
var executableName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? DotnetConstants.WindowsExecutableName : DotnetConstants.LinuxExecutableName;
487586

488-
return Path.Combine(funcExecutableDirectory, InProc8DirectoryName, executableName);
587+
return Path.Combine(funcExecutableDirectory, (isNet8 ? DotnetConstants.InProc8DirectoryName: DotnetConstants.InProc6DirectoryName), executableName);
489588
}
490589

491-
private Task StartInProc8AsChildProcessAsync()
590+
private void StartHostAsChildProcess(bool shouldStartNet8ChildProcess)
492591
{
493592
if (VerboseLogging == true)
494593
{
495-
ColoredConsole.WriteLine(VerboseColor($"Starting child process for in-process model host."));
594+
ColoredConsole.WriteLine(VerboseColor($"Starting child process for {(shouldStartNet8ChildProcess ? DotnetConstants.InProc8HostRuntime : DotnetConstants.InProc6HostRuntime)} model host."));
496595
}
497596

498597
var commandLineArguments = string.Join(" ", Environment.GetCommandLineArgs().Skip(1));
499598
var tcs = new TaskCompletionSource();
500599

501-
var inProc8FuncExecutablePath = GetInProcNet8ExecutablePath();
600+
var funcExecutablePath = GetInProcExecutablePath(isNet8: shouldStartNet8ChildProcess);
502601

503-
EnsureNet8FuncExecutablePresent(inProc8FuncExecutablePath);
602+
EnsureFuncExecutablePresent(funcExecutablePath, shouldStartNet8ChildProcess);
504603

505-
var inprocNet8ChildProcessInfo = new ProcessStartInfo
604+
var childProcessInfo = new ProcessStartInfo
506605
{
507-
FileName = inProc8FuncExecutablePath,
606+
FileName = funcExecutablePath,
508607
Arguments = $"{commandLineArguments} --no-build",
509608
WorkingDirectory = Environment.CurrentDirectory,
510609
UseShellExecute = false,
@@ -515,7 +614,7 @@ private Task StartInProc8AsChildProcessAsync()
515614

516615
try
517616
{
518-
var childProcess = Process.Start(inprocNet8ChildProcessInfo);
617+
var childProcess = Process.Start(childProcessInfo);
519618
if (VerboseLogging == true)
520619
{
521620
ColoredConsole.WriteLine(VerboseColor($"Started child process with ID: {childProcess.Id}"));
@@ -546,23 +645,21 @@ private Task StartInProc8AsChildProcessAsync()
546645
}
547646
catch (Exception ex)
548647
{
549-
throw new CliException($"Failed to start the in-process model host. {ex.Message}");
648+
throw new CliException($"Failed to start the {(shouldStartNet8ChildProcess ? DotnetConstants.InProc8HostRuntime : DotnetConstants.InProc6HostRuntime)} model host. {ex.Message}");
550649
}
551-
552-
return tcs.Task;
553650
}
554651

555-
private void EnsureNet8FuncExecutablePresent(string inProc8FuncExecutablePath)
652+
private void EnsureFuncExecutablePresent(string funcExecutablePath, bool isInProcNet8)
556653
{
557-
bool net8ExeExist = File.Exists(inProc8FuncExecutablePath);
654+
bool funcExeExist = File.Exists(funcExecutablePath);
558655
if (VerboseLogging == true)
559656
{
560-
ColoredConsole.WriteLine(VerboseColor($"{inProc8FuncExecutablePath} {(net8ExeExist ? "present" : "not present")} "));
657+
ColoredConsole.WriteLine(VerboseColor($"{funcExecutablePath} {(funcExeExist ? "present" : "not present")} "));
561658
}
562659

563-
if (!net8ExeExist)
660+
if (!funcExeExist)
564661
{
565-
throw new CliException($"Failed to locate the in-process model host at {inProc8FuncExecutablePath}");
662+
throw new CliException($"Failed to locate the {(isInProcNet8 ? DotnetConstants.InProc8HostRuntime : DotnetConstants.InProc6HostRuntime)} model host at {funcExecutablePath}");
566663
}
567664
}
568665

src/Azure.Functions.Cli/Actions/LocalActions/CreateFunctionAction.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
using Azure.Functions.Cli.Interfaces;
1414
using Colors.Net;
1515
using Fclp;
16-
using ImTools;
1716
using Microsoft.Azure.AppService.Proxy.Common.Context;
1817
using Microsoft.Azure.WebJobs.Extensions.Http;
1918
using Microsoft.Azure.WebJobs.Script;

0 commit comments

Comments
 (0)