Skip to content

Set PSModulePath for first PS instance early #1038

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: Francisco-Gamino/new-programming-model-latest-dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/FunctionLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,20 @@ internal static void ClearLoadedFunctions()
/// Setup the well known paths about the FunctionApp.
/// This method is called only once during the code start.
/// </summary>
internal static void SetupWellKnownPaths(string functionAppRootPath, string managedDependenciesPath)
internal static void SetupWellKnownPaths(string functionAppRootPath, string managedDependenciesPath, bool functionAppRootIsUnknown)
{
var workerLevelModulesPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Modules");

if (functionAppRootIsUnknown)
{
FunctionModulePath = workerLevelModulesPath;
return;
}

FunctionAppRootPath = functionAppRootPath;

// Resolve module paths
var appLevelModulesPath = Path.Join(FunctionAppRootPath, "Modules");
var workerLevelModulesPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Modules");
FunctionModulePath = $"{appLevelModulesPath}{Path.PathSeparator}{workerLevelModulesPath}";

// Add the managed dependencies folder path
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#
# Copyright (c) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.
#

class Function : Attribute {
[string]$Name
}

class HttpTrigger : Attribute {
[string]$AuthLevel
[string[]]$Methods
[string]$Route
}

class HttpOutput : Attribute {
[string]$Name
}

class TimerTrigger : Attribute {
[string]$Chron
}

class EventGridTrigger : Attribute {
EventGridTrigger() { }
}

class DurableClient : Attribute {
[string]$Name
}

class OrchestrationTrigger : Attribute {
}

class ActivityTrigger : Attribute {
}

class EventHubTrigger : Attribute {
[string]$EventHubName
[string]$ConsumerGroup
[string]$Cardinality
[string]$Connection
}

class EventHubOutput : Attribute {
[string]$Name
[string]$EventHubName
[string]$Connection
}

class InputBinding : Attribute {
[string]$Type
[string]$Name
}

class OutputBinding : Attribute {
[string]$Type
[string]$Name
}

class AdditionalInformation : Attribute {
[string]$BindingName
[string]$Name
$Value
}
Binary file not shown.
121 changes: 121 additions & 0 deletions src/Modules/AzureFunctions.PowerShell.SDK/0.0.3/PSGetModuleInfo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
<Obj RefId="0">
<TN RefId="0">
<T>Microsoft.PowerShell.Commands.PSRepositoryItemInfo</T>
<T>System.Management.Automation.PSCustomObject</T>
<T>System.Object</T>
</TN>
<MS>
<S N="Name">AzureFunctions.PowerShell.SDK</S>
<S N="Version">0.0.3</S>
<S N="Type">Module</S>
<S N="Description">This module contains utilities for writing and parsing Azure Functions written in PowerShell and it is to be used within the PowerShell language worker</S>
<S N="Author">Microsoft Corporation</S>
<S N="CompanyName">Francisco-Gamino</S>
<S N="Copyright">(c) 2022 Microsoft. All rights reserved.</S>
<DT N="PublishedDate">2023-06-05T03:55:02-06:00</DT>
<Nil N="InstalledDate" />
<Nil N="UpdatedDate" />
<Nil N="LicenseUri" />
<Nil N="ProjectUri" />
<Nil N="IconUri" />
<Obj N="Tags" RefId="1">
<TN RefId="1">
<T>System.Object[]</T>
<T>System.Array</T>
<T>System.Object</T>
</TN>
<LST>
<S>PSModule</S>
<S>PSEdition_Core</S>
</LST>
</Obj>
<Obj N="Includes" RefId="2">
<TN RefId="2">
<T>System.Collections.Hashtable</T>
<T>System.Object</T>
</TN>
<DCT>
<En>
<S N="Key">Cmdlet</S>
<Obj N="Value" RefId="3">
<TNRef RefId="1" />
<LST>
<S>Get-FunctionsMetadata</S>
</LST>
</Obj>
</En>
<En>
<S N="Key">Workflow</S>
<Obj N="Value" RefId="4">
<TNRef RefId="1" />
<LST />
</Obj>
</En>
<En>
<S N="Key">Function</S>
<Ref N="Value" RefId="4" />
</En>
<En>
<S N="Key">Command</S>
<Obj N="Value" RefId="5">
<TNRef RefId="1" />
<LST>
<S>Get-FunctionsMetadata</S>
</LST>
</Obj>
</En>
<En>
<S N="Key">DscResource</S>
<Ref N="Value" RefId="4" />
</En>
<En>
<S N="Key">RoleCapability</S>
<Ref N="Value" RefId="4" />
</En>
</DCT>
</Obj>
<Nil N="PowerShellGetFormatVersion" />
<S N="ReleaseNotes"># 0.0.3_x000D__x000A_ Initial Release.</S>
<Obj N="Dependencies" RefId="6">
<TNRef RefId="1" />
<LST />
</Obj>
<S N="RepositorySourceLocation">https://www.powershellgallery.com/api/v2</S>
<S N="Repository">PSGallery</S>
<S N="PackageManagementProvider">NuGet</S>
<Obj N="AdditionalMetadata" RefId="7">
<TN RefId="3">
<T>System.Management.Automation.PSCustomObject</T>
<T>System.Object</T>
</TN>
<MS>
<S N="copyright">(c) 2022 Microsoft. All rights reserved.</S>
<S N="description">This module contains utilities for writing and parsing Azure Functions written in PowerShell and it is to be used within the PowerShell language worker</S>
<S N="requireLicenseAcceptance">False</S>
<S N="releaseNotes"># 0.0.3_x000D__x000A_ Initial Release.</S>
<S N="isLatestVersion">True</S>
<S N="isAbsoluteLatestVersion">True</S>
<S N="versionDownloadCount">215</S>
<S N="downloadCount">261</S>
<S N="packageSize">19534</S>
<S N="published">6/5/2023 3:55:02 AM -06:00</S>
<S N="created">6/5/2023 3:55:02 AM -06:00</S>
<S N="lastUpdated">2/15/2024 7:12:43 AM -07:00</S>
<S N="tags">PSModule PSEdition_Core PSCmdlet_Get-FunctionsMetadata PSCommand_Get-FunctionsMetadata PSIncludes_Cmdlet</S>
<S N="developmentDependency">False</S>
<S N="updated">2024-02-15T07:12:43Z</S>
<S N="NormalizedVersion">0.0.3</S>
<S N="Authors">Microsoft Corporation</S>
<S N="IsPrerelease">false</S>
<S N="ItemType">Module</S>
<S N="FileList">AzureFunctions.PowerShell.SDK.nuspec|AzureFunctions.AttributeDefinitions.ps1|AzureFunctions.PowerShell.SDK.dll|AzureFunctions.PowerShell.SDK.psd1</S>
<S N="GUID">1d075857-4919-4ede-bb43-bb193483a280</S>
<S N="PowerShellVersion">7.2</S>
<S N="CompanyName">Microsoft Corporation</S>
</MS>
</Obj>
<S N="InstalledLocation">D:\repos\azure-functions-powershell-worker\src\Modules\AzureFunctions.PowerShell.SDK\0.0.3</S>
</MS>
</Obj>
</Objs>
2 changes: 1 addition & 1 deletion src/PowerShell/PowerShellManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ public Hashtable InvokeFunction(

try
{
if(functionInfo.DurableFunctionInfo.IsOrchestrationFunction)
if (functionInfo.DurableFunctionInfo.IsOrchestrationFunction)
{
return durableFunctionsUtils.InvokeOrchestrationFunction();
}
Expand Down
4 changes: 2 additions & 2 deletions src/RequestProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ private StreamingMessage ProcessFunctionMetadataRequest(StreamingMessage request
rpcLogger.SetContext(request.RequestId, null);

_functionAppRootPath = request.FunctionsMetadataRequest.FunctionAppDirectory;
response.FunctionMetadataResponse.FunctionMetadataResults.AddRange(WorkerIndexingHelper.IndexFunctions(_functionAppRootPath, rpcLogger));
response.FunctionMetadataResponse.FunctionMetadataResults.AddRange(WorkerIndexingHelper.IndexFunctions(_functionAppRootPath, rpcLogger, _firstPwshInstance));

return response;
}
Expand Down Expand Up @@ -574,7 +574,7 @@ private static void BindOutputFromResult(InvocationResponse response, AzFunction

private void SetupAppRootPathAndModulePath(string functionAppRootPath, string managedDependenciesPath)
{
FunctionLoader.SetupWellKnownPaths(functionAppRootPath, managedDependenciesPath);
FunctionLoader.SetupWellKnownPaths(functionAppRootPath, managedDependenciesPath, false);

if (FunctionLoader.FunctionAppRootPath == null)
{
Expand Down
30 changes: 19 additions & 11 deletions src/WorkerIndexing/WorkerIndexingHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ internal class WorkerIndexingHelper
const string AzureFunctionsPowerShellSDKModuleName = "AzureFunctions.PowerShell.SDK";
private static readonly ErrorRecordFormatter _errorRecordFormatter = new ErrorRecordFormatter();

internal static IEnumerable<RpcFunctionMetadata> IndexFunctions(string functionAppRootPath, ILogger logger)
internal static IEnumerable<RpcFunctionMetadata> IndexFunctions(string functionAppRootPath, ILogger logger, System.Management.Automation.PowerShell _firstPwshInstance)
{
if (string.IsNullOrWhiteSpace(functionAppRootPath))
{
Expand All @@ -35,6 +35,14 @@ internal static IEnumerable<RpcFunctionMetadata> IndexFunctions(string functionA

List<RpcFunctionMetadata> indexedFunctions = new List<RpcFunctionMetadata>();

// A hacky way to add the worker modules into the env only
FunctionLoader.SetupWellKnownPaths(null, null, true);

_firstPwshInstance.AddCommand("Microsoft.PowerShell.Management\\Set-Content")
.AddParameter("Path", "env:PSModulePath")
.AddParameter("Value", FunctionLoader.FunctionModulePath)
.InvokeAndClearCommands();

// This is not the correct way to deal with getting a runspace for the cmdlet.

// Firstly, creating a runspace is expensive. If we are going to generate a runspace, it should be done on
Expand All @@ -55,11 +63,11 @@ internal static IEnumerable<RpcFunctionMetadata> IndexFunctions(string functionA
// 3. Continue using a new runspace for invoking Get-FunctionsMetadata, but initialize it in worker init and
// point the PsModulePath to the module path bundled with the worker.

InitialSessionState initial = InitialSessionState.CreateDefault();
Runspace runspace = RunspaceFactory.CreateRunspace(initial);
runspace.Open();
System.Management.Automation.PowerShell _powershell = System.Management.Automation.PowerShell.Create();
_powershell.Runspace = runspace;
//InitialSessionState initial = InitialSessionState.CreateDefault();
//Runspace runspace = RunspaceFactory.CreateRunspace(initial);
//runspace.Open();
//System.Management.Automation.PowerShell _powershell = System.Management.Automation.PowerShell.Create();
//_powershell.Runspace = runspace;

string outputString = string.Empty;
Exception exception = null;
Expand All @@ -70,9 +78,9 @@ internal static IEnumerable<RpcFunctionMetadata> IndexFunctions(string functionA

try
{
_powershell.AddCommand(GetFunctionsMetadataCmdletName).AddArgument(functionAppRootPath);
results = _powershell.Invoke();
cmdletExecutionHadErrors = _powershell.HadErrors;
_firstPwshInstance.AddCommand(GetFunctionsMetadataCmdletName).AddArgument(functionAppRootPath);
results = _firstPwshInstance.Invoke();
cmdletExecutionHadErrors = _firstPwshInstance.HadErrors;
}
catch (Exception ex)
{
Expand All @@ -88,7 +96,7 @@ internal static IEnumerable<RpcFunctionMetadata> IndexFunctions(string functionA
}
else if (cmdletExecutionHadErrors)
{
var errorCollection = _powershell.Streams.Error;
var errorCollection = _firstPwshInstance.Streams.Error;

var stringBuilder = new StringBuilder();
foreach (var errorRecord in errorCollection)
Expand All @@ -106,7 +114,7 @@ internal static IEnumerable<RpcFunctionMetadata> IndexFunctions(string functionA
throw new Exception(errorMsg);
}

_powershell.Commands.Clear();
_firstPwshInstance.Commands.Clear();
}

// TODO: The GetFunctionsMetadataCmdlet should never return more than one result. Make sure that this is the case and remove this code.
Expand Down
2 changes: 1 addition & 1 deletion test/Unit/Modules/HelperModuleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ static HelperModuleTests()
};

var funcLoadReq = new FunctionLoadRequest { FunctionId = "FunctionId", Metadata = rpcFuncMetadata };
FunctionLoader.SetupWellKnownPaths(funcLoadReq.Metadata.Directory, managedDependenciesPath: null);
FunctionLoader.SetupWellKnownPaths(funcLoadReq.Metadata.Directory, managedDependenciesPath: null, false);
s_pwsh = Utils.NewPwshInstance();
s_funcInfo = new AzFunctionInfo(rpcFuncMetadata);
}
Expand Down
2 changes: 1 addition & 1 deletion test/Unit/PowerShell/PowerShellManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ static PowerShellManagerTests()
};

s_functionLoadRequest = new FunctionLoadRequest { FunctionId = "FunctionId", Metadata = rpcFunctionMetadata };
FunctionLoader.SetupWellKnownPaths(s_functionLoadRequest.Metadata.Directory, managedDependenciesPath: null);
FunctionLoader.SetupWellKnownPaths(s_functionLoadRequest.Metadata.Directory, managedDependenciesPath: null, false);
}

// Have a single place to get a PowerShellManager for testing.
Expand Down