Skip to content

Commit c09a203

Browse files
authored
Check environment for 'FUNCTIONS_WORKER_RUNTIME' on start (#4219)
1 parent 8961bad commit c09a203

File tree

3 files changed

+73
-139
lines changed

3 files changed

+73
-139
lines changed

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@ private void ValidateAndBuildHostJsonConfigurationIfFileExists(ScriptApplication
668668
private async Task PreRunConditions()
669669
{
670670
EnsureWorkerRuntimeIsSet();
671+
671672
if (GlobalCoreToolsSettings.CurrentWorkerRuntime == WorkerRuntime.python)
672673
{
673674
var pythonVersion = await PythonHelpers.GetEnvironmentPythonVersion();
@@ -813,23 +814,22 @@ internal static bool ConnectionExists(IEnumerable<KeyValuePair<string, string>>
813814

814815
private void EnsureWorkerRuntimeIsSet()
815816
{
816-
var workerRuntimeSettingValue = _secretsManager.GetSecrets().FirstOrDefault(s => s.Key.Equals(Constants.FunctionsWorkerRuntime, StringComparison.OrdinalIgnoreCase)).Value;
817-
if (workerRuntimeSettingValue is not null)
817+
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(Constants.FunctionsWorkerRuntime))
818+
|| _secretsManager.GetSecrets().Any(s => s.Key.Equals(Constants.FunctionsWorkerRuntime, StringComparison.OrdinalIgnoreCase)))
818819
{
819820
return;
820821
}
821822

822-
if (GlobalCoreToolsSettings.CurrentWorkerRuntimeOrNone == WorkerRuntime.None)
823+
if (GlobalCoreToolsSettings.CurrentWorkerRuntimeOrNone is WorkerRuntime.None)
823824
{
824825
SelectionMenuHelper.DisplaySelectionWizardPrompt("worker runtime");
825826
IDictionary<WorkerRuntime, string> workerRuntimeToDisplayString = WorkerRuntimeLanguageHelper.GetWorkerToDisplayStrings();
826827
string workerRuntimeDisplay = SelectionMenuHelper.DisplaySelectionWizard(workerRuntimeToDisplayString.Values);
827828
GlobalCoreToolsSettings.CurrentWorkerRuntime = workerRuntimeToDisplayString.FirstOrDefault(wr => wr.Value.Equals(workerRuntimeDisplay)).Key;
828829
}
829830

830-
var workerRuntime = WorkerRuntimeLanguageHelper.GetRuntimeMoniker(GlobalCoreToolsSettings.CurrentWorkerRuntime);
831-
_secretsManager.SetSecret(Constants.FunctionsWorkerRuntime, workerRuntime);
832-
ColoredConsole.WriteLine(WarningColor($"'{workerRuntime}' has been set in your local.settings.json"));
831+
// Update local.settings.json
832+
WorkerRuntimeLanguageHelper.SetWorkerRuntime(_secretsManager, GlobalCoreToolsSettings.CurrentWorkerRuntime.ToString());
833833
}
834834
}
835835
}

src/Azure.Functions.Cli/Helpers/WorkerRuntimeLanguageHelper.cs

+10-6
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,9 @@ public static IEnumerable<string> LanguagesForWorker(WorkerRuntime worker)
166166

167167
public static WorkerRuntime GetCurrentWorkerRuntimeLanguage(ISecretsManager secretsManager)
168168
{
169-
var setting = secretsManager.GetSecrets().FirstOrDefault(s => s.Key.Equals(Constants.FunctionsWorkerRuntime, StringComparison.OrdinalIgnoreCase)).Value;
169+
var setting = Environment.GetEnvironmentVariable(Constants.FunctionsWorkerRuntime)
170+
?? secretsManager.GetSecrets().FirstOrDefault(s => s.Key.Equals(Constants.FunctionsWorkerRuntime, StringComparison.OrdinalIgnoreCase)).Value;
171+
170172
try
171173
{
172174
return NormalizeWorkerRuntime(setting);
@@ -179,14 +181,16 @@ public static WorkerRuntime GetCurrentWorkerRuntimeLanguage(ISecretsManager secr
179181

180182
internal static WorkerRuntime SetWorkerRuntime(ISecretsManager secretsManager, string language)
181183
{
182-
var worker = NormalizeWorkerRuntime(language);
184+
var workerRuntime = NormalizeWorkerRuntime(language);
185+
var runtimeMoniker = GetRuntimeMoniker(workerRuntime);
186+
187+
secretsManager.SetSecret(Constants.FunctionsWorkerRuntime, runtimeMoniker);
183188

184-
secretsManager.SetSecret(Constants.FunctionsWorkerRuntime, worker.ToString());
185189
ColoredConsole
186-
.WriteLine(WarningColor("Starting from 2.0.1-beta.26 it's required to set a language for your project in your settings"))
187-
.WriteLine(WarningColor($"'{worker}' has been set in your local.settings.json"));
190+
.WriteLine(WarningColor("Starting from 2.0.1-beta.26 it's required to set a language for your project in your settings."))
191+
.WriteLine(WarningColor($"Worker runtime '{runtimeMoniker}' has been set in '{SecretsManager.AppSettingsFilePath}'."));
188192

189-
return worker;
193+
return workerRuntime;
190194
}
191195

192196
public static string GetDefaultTemplateLanguageFromWorker(WorkerRuntime worker)

test/Azure.Functions.Cli.Tests/E2E/StartTests.cs

+57-127
Original file line numberDiff line numberDiff line change
@@ -1708,144 +1708,74 @@ await CliTester.Run(new RunConfiguration[]
17081708
}
17091709

17101710
[Theory]
1711-
[InlineData("dotnet-isolated")]
1712-
[InlineData("node")]
1713-
public async Task Start_MissingLocalSettingsJson_SuccessfulFunctionExecution(string language)
1711+
[InlineData("dotnet-isolated", "--dotnet-isolated", "HttpTriggerFunc: [GET,POST] http://localhost:", true, false)] // Runtime parameter set (dni), successful startup & invocation
1712+
[InlineData("node", "--node", "HttpTriggerFunc: [GET,POST] http://localhost:", true, false)] // Runtime parameter set (node), successful startup & invocation
1713+
[InlineData("dotnet", "--worker-runtime None", $"Use the up/down arrow keys to select a worker runtime:", false, false)] // Runtime parameter set to None, worker runtime prompt displayed
1714+
[InlineData("dotnet", "", $"Use the up/down arrow keys to select a worker runtime:", false, false)] // Runtime parameter not provided, worker runtime prompt displayed
1715+
[InlineData("dotnet-isolated", "", "HttpTriggerFunc: [GET,POST] http://localhost:", true, true)] // Runtime value is set via environment variable, successful startup & invocation
1716+
public async Task Start_MissingLocalSettingsJson_BehavesAsExpected(string language, string runtimeParameter, string expectedOutput, bool invokeFunction, bool setRuntimeViaEnvironment)
17141717
{
1715-
await CliTester.Run(new RunConfiguration[]
1718+
try
17161719
{
1717-
new RunConfiguration
1720+
if (setRuntimeViaEnvironment)
17181721
{
1719-
Commands = new[]
1720-
{
1721-
$"init . --worker-runtime {language}",
1722-
$"new --template Httptrigger --name HttpTriggerFunc",
1723-
},
1724-
CommandTimeout = TimeSpan.FromSeconds(300),
1725-
},
1726-
new RunConfiguration
1722+
Environment.SetEnvironmentVariable("FUNCTIONS_WORKER_RUNTIME", "dotnet-isolated");
1723+
}
1724+
1725+
await CliTester.Run(new RunConfiguration[]
17271726
{
1728-
PreTest = (workingDir) =>
1727+
new RunConfiguration
17291728
{
1730-
var LocalSettingsJson = Path.Combine(workingDir, "local.settings.json");
1731-
File.Delete(LocalSettingsJson);
1732-
},
1733-
Commands = new[]
1734-
{
1735-
$"start --{language} --port {_funcHostPort}",
1736-
},
1737-
ExpectExit = false,
1738-
OutputContains = new[]
1739-
{
1740-
$"local.settings.json",
1741-
"Functions:",
1742-
$"HttpTriggerFunc: [GET,POST] http://localhost:{_funcHostPort}/api/HttpTriggerFunc"
1743-
},
1744-
OutputDoesntContain = new string[]
1745-
{
1746-
"Initializing function HTTP routes"
1729+
Commands = new[]
1730+
{
1731+
$"init . --worker-runtime {language}",
1732+
$"new --template Httptrigger --name HttpTriggerFunc",
1733+
},
1734+
CommandTimeout = TimeSpan.FromSeconds(300),
17471735
},
1748-
Test = async (_, p,_) =>
1736+
new RunConfiguration
17491737
{
1750-
using (var client = new HttpClient() { BaseAddress = new Uri($"http://localhost:{_funcHostPort}/") })
1738+
PreTest = (workingDir) =>
17511739
{
1752-
(await WaitUntilReady(client)).Should().BeTrue(because: _serverNotReady);
1753-
var response = await client.GetAsync("/api/HttpTriggerFunc?name=Test");
1754-
response.StatusCode.Should().Be(HttpStatusCode.OK);
1755-
await Task.Delay(TimeSpan.FromSeconds(2));
1756-
p.Kill();
1740+
var localSettingsJson = Path.Combine(workingDir, "local.settings.json");
1741+
File.Delete(localSettingsJson);
1742+
},
1743+
Commands = new[]
1744+
{
1745+
$"start {runtimeParameter} --port {_funcHostPort}",
1746+
},
1747+
ExpectExit = false,
1748+
OutputContains = new[]
1749+
{
1750+
expectedOutput
1751+
},
1752+
Test = async (_, p,_) =>
1753+
{
1754+
if (invokeFunction)
1755+
{
1756+
using (var client = new HttpClient() { BaseAddress = new Uri($"http://localhost:{_funcHostPort}/") })
1757+
{
1758+
(await WaitUntilReady(client)).Should().BeTrue(because: _serverNotReady);
1759+
var response = await client.GetAsync("/api/HttpTriggerFunc?name=Test");
1760+
response.StatusCode.Should().Be(HttpStatusCode.OK);
1761+
await Task.Delay(TimeSpan.FromSeconds(2));
1762+
p.Kill();
1763+
}
1764+
}
1765+
else
1766+
{
1767+
await Task.Delay(TimeSpan.FromSeconds(2));
1768+
p.Kill();
1769+
}
1770+
17571771
}
17581772
}
1759-
}
1760-
}, _output);
1761-
}
1762-
1763-
[Fact]
1764-
public async Task Start_MissingLocalSettingsJson_Runtime_None_HandledAsExpected()
1765-
{
1766-
await CliTester.Run(new RunConfiguration[]
1767-
{
1768-
new RunConfiguration
1769-
{
1770-
Commands = new[]
1771-
{
1772-
$"init . --worker-runtime dotnet",
1773-
$"new --template Httptrigger --name HttpTriggerFunc",
1774-
},
1775-
CommandTimeout = TimeSpan.FromSeconds(300),
1776-
},
1777-
new RunConfiguration
1778-
{
1779-
PreTest = (workingDir) =>
1780-
{
1781-
var LocalSettingsJson = Path.Combine(workingDir, "local.settings.json");
1782-
File.Delete(LocalSettingsJson);
1783-
},
1784-
Commands = new[]
1785-
{
1786-
$"start --worker-runtime None --port {_funcHostPort}",
1787-
},
1788-
ExpectExit = false,
1789-
OutputContains = new[]
1790-
{
1791-
$"Use the up/down arrow keys to select a worker runtime:"
1792-
},
1793-
OutputDoesntContain = new string[]
1794-
{
1795-
"Initializing function HTTP routes"
1796-
},
1797-
Test = async (_, p,_) =>
1798-
{
1799-
await Task.Delay(TimeSpan.FromSeconds(2));
1800-
p.Kill();
1801-
}
1802-
}
1803-
}, _output);
1804-
1805-
}
1806-
1807-
[Fact]
1808-
public async Task Start_MissingLocalSettingsJson_Runtime_NotProvided_HandledAsExpected()
1809-
{
1810-
await CliTester.Run(new RunConfiguration[]
1773+
}, _output);
1774+
}
1775+
finally
18111776
{
1812-
new RunConfiguration
1813-
{
1814-
Commands = new[]
1815-
{
1816-
$"init . --worker-runtime dotnet",
1817-
$"new --template Httptrigger --name HttpTriggerFunc",
1818-
},
1819-
CommandTimeout = TimeSpan.FromSeconds(300),
1820-
},
1821-
new RunConfiguration
1822-
{
1823-
PreTest = (workingDir) =>
1824-
{
1825-
var LocalSettingsJson = Path.Combine(workingDir, "local.settings.json");
1826-
File.Delete(LocalSettingsJson);
1827-
},
1828-
Commands = new[]
1829-
{
1830-
$"start --port {_funcHostPort}",
1831-
},
1832-
ExpectExit = false,
1833-
OutputContains = new[]
1834-
{
1835-
$"Use the up/down arrow keys to select a worker runtime:"
1836-
},
1837-
OutputDoesntContain = new string[]
1838-
{
1839-
"Initializing function HTTP routes"
1840-
},
1841-
Test = async (_, p,_) =>
1842-
{
1843-
await Task.Delay(TimeSpan.FromSeconds(2));
1844-
p.Kill();
1845-
}
1846-
}
1847-
}, _output);
1848-
1777+
Environment.SetEnvironmentVariable("FUNCTIONS_WORKER_RUNTIME", null);
1778+
}
18491779
}
18501780

18511781
private async Task<bool> WaitUntilReady(HttpClient client)

0 commit comments

Comments
 (0)