This commit is contained in:
Tyler Leonhardt 2018-08-23 19:25:45 -07:00
Родитель 91b2bf8516
Коммит e395476fea
16 изменённых файлов: 254 добавлений и 237 удалений

Просмотреть файл

@ -1,6 +1,5 @@
{
"disabled": false,
"entryPoint":"FunctionName",
"bindings": [
{
"authLevel": "function",
@ -15,7 +14,7 @@
{
"type": "http",
"direction": "out",
"name": "$return"
"name": "res"
}
]
}

Просмотреть файл

@ -1,6 +1,12 @@
function FunctionName {
$global:res = $req.GetHttpResponseContext()
"hello verbose"
$res.Json('{"Hello":"World"}')
$res.SetHeader("foo", "bar")
$name = 'World'
if($req.Query.Name) {
$name = $req.Query.Name
}
Write-Verbose "Hello $name" -Verbose
Write-Warning "Warning $name"
$res = [HttpResponseContext]@{
Body = @{ Hello = $name }
ContentType = 'application/json'
}

Просмотреть файл

@ -21,6 +21,9 @@ namespace Azure.Functions.PowerShell.Worker.Messaging
public async Task WriteAsync(StreamingMessage message)
{
if(isDisposed) return;
// Wait for the handle to be released because we can't have
// more than one message being sent at the same time
await _writeStreamHandle.WaitAsync();
try
{

Просмотреть файл

@ -24,12 +24,10 @@ namespace Microsoft.Azure.Functions.PowerShellWorker
{
Bindings.Add(binding.Key, binding.Value);
// Only add Out and InOut bindings to the OutputBindings
if (binding.Value.Direction != BindingInfo.Types.Direction.In)
{
if(binding.Value.Type == "http")
{
HttpOutputName = binding.Key;
}if(binding.Value.Type == "http")
{
HttpOutputName = binding.Key;
}

Просмотреть файл

@ -13,10 +13,5 @@ namespace Microsoft.Azure.Functions.PowerShellWorker
public MapField<string, string> Params {get; set;}
public object Body {get; set;}
public object RawBody {get; set;}
public HttpResponseContext GetHttpResponseContext()
{
return new HttpResponseContext();
}
}
}

Просмотреть файл

@ -1,3 +1,4 @@
using System.Collections;
using Google.Protobuf.Collections;
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
@ -5,78 +6,10 @@ namespace Microsoft.Azure.Functions.PowerShellWorker
{
public class HttpResponseContext
{
#region properties
public string StatusCode {get; set;} = "200";
public MapField<string, string> Headers {get; set;} = new MapField<string,string>();
public TypedData Body {get; set;} = new TypedData { String = "" };
public Hashtable Headers {get; set;} = new Hashtable();
public object Body {get; set;}
public string ContentType {get; set;} = "text/plain";
public bool EnableContentNegotiation {get; set;} = false;
#endregion
#region Helper functions for user to use to set data
public HttpResponseContext Header(string field, string value) =>
SetHeader(field, value);
public HttpResponseContext SetHeader(string field, string value)
{
Headers.Add(field, value);
return this;
}
public string GetHeader(string field) =>
Headers[field];
public HttpResponseContext RemoveHeader(string field)
{
Headers.Remove(field);
return this;
}
public HttpResponseContext Status(int statusCode) =>
SetStatus(statusCode);
public HttpResponseContext Status(string statusCode) =>
SetStatus(statusCode);
public HttpResponseContext SetStatus(int statusCode) =>
SetStatus(statusCode);
public HttpResponseContext SetStatus(string statusCode)
{
StatusCode = statusCode;
return this;
}
public HttpResponseContext Type(string type) =>
SetHeader("content-type", type);
public HttpResponseContext SetContentType(string type) =>
SetHeader("content-type", type);
public HttpResponseContext Send(int val)
{
Body = new TypedData
{
Int = val
};
return this;
}
public HttpResponseContext Send(double val)
{
Body = new TypedData
{
Double = val
};
return this;
}
public HttpResponseContext Send(string val)
{
Body = new TypedData
{
String = val
};
return this;
}
public HttpResponseContext Json(string val) {
Body = new TypedData
{
Json = val
};
return Type("application/json");
}
#endregion
}
}

Просмотреть файл

@ -10,8 +10,11 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// applications. Not all members are implemented. Those that aren't throw a
/// NotImplementedException.
/// </summary>
internal class Host : PSHost
internal class AzureFunctionsHost : PSHost
{
/// <summary>
/// The private reference of the logger.
/// </summary>
private RpcLogger _logger;
/// <summary>
@ -80,15 +83,14 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// </summary>
public override Version Version => new Version(1, 0, 0, 0);
public Host(RpcLogger logger)
public AzureFunctionsHost(RpcLogger logger)
{
_logger = logger;
HostUI = new HostUserInterface(logger);
}
/// <summary>
/// Not implemented by this example class. The call fails with an exception.
/// Not implemented by this class. The call fails with an exception.
/// </summary>
public override void EnterNestedPrompt()
{
@ -96,7 +98,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
}
/// <summary>
/// Not implemented by this example class. The call fails with an exception.
/// Not implemented by this class. The call fails with an exception.
/// </summary>
public override void ExitNestedPrompt()
{
@ -106,7 +108,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// <summary>
/// This API is called before an external application process is started. Typically
/// it's used to save state that the child process may alter so the parent can
/// restore that state when the child exits. In this sample, we don't need this so
/// restore that state when the child exits. In this, we don't need this so
/// the method simple returns.
/// </summary>
public override void NotifyBeginApplication()
@ -116,8 +118,8 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// <summary>
/// This API is called after an external application process finishes. Typically
/// it's used to restore state that the child process may have altered. In this
/// sample, we don't need this so the method simple returns.
/// it's used to restore state that the child process may have altered. In this,
/// we don't need this so the method simple returns.
/// </summary>
public override void NotifyEndApplication()
{

Просмотреть файл

@ -15,6 +15,9 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// </summary>
internal class HostUserInterface : PSHostUserInterface
{
/// <summary>
/// The private reference of the logger.
/// </summary>
private RpcLogger _logger;
/// <summary>
@ -40,7 +43,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// <param name="message">The text of the prompt.</param>
/// <param name="descriptions">A collection of FieldDescription objects that
/// describe each field of the prompt.</param>
/// <returns>Throws a NotImplementedException exception.</returns>
/// <returns>Throws a NotImplementedException exception because we don't need a prompt.</returns>
public override Dictionary<string, PSObject> Prompt(string caption, string message, System.Collections.ObjectModel.Collection<FieldDescription> descriptions)
{
throw new NotImplementedException("The method or operation is not implemented.");
@ -55,7 +58,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// each choice.</param>
/// <param name="defaultChoice">The index of the label in the Choices parameter
/// collection. To indicate no default choice, set to -1.</param>
/// <returns>Throws a NotImplementedException exception.</returns>
/// <returns>Throws a NotImplementedException exception because we don't need a prompt.</returns>
public override int PromptForChoice(string caption, string message, System.Collections.ObjectModel.Collection<ChoiceDescription> choices, int defaultChoice)
{
throw new NotImplementedException("The method or operation is not implemented.");
@ -69,7 +72,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// <param name="message">The text of the message.</param>
/// <param name="userName">The user name whose credential is to be prompted for.</param>
/// <param name="targetName">The name of the target for which the credential is collected.</param>
/// <returns>Throws a NotImplementedException exception.</returns>
/// <returns>Throws a NotImplementedException exception because we don't need a prompt.</returns>
public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName)
{
throw new NotImplementedException("The method or operation is not implemented.");
@ -88,7 +91,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// identifies the type of credentials that can be returned.</param>
/// <param name="options">A PSCredentialUIOptions constant that identifies the UI
/// behavior when it gathers the credentials.</param>
/// <returns>Throws a NotImplementedException exception.</returns>
/// <returns>Throws a NotImplementedException exception because we don't need a prompt.</returns>
public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName, PSCredentialTypes allowedCredentialTypes, PSCredentialUIOptions options)
{
throw new NotImplementedException("The method or operation is not implemented.");
@ -98,7 +101,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// Reads characters that are entered by the user until a newline
/// (carriage return) is encountered.
/// </summary>
/// <returns>The characters that are entered by the user.</returns>
/// <returns>Throws a NotImplemented exception because we are in a non-interactive experience.</returns>
public override string ReadLine()
{
throw new NotImplementedException("The method or operation is not implemented.");
@ -108,7 +111,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// Reads characters entered by the user until a newline (carriage return)
/// is encountered and returns the characters as a secure string.
/// </summary>
/// <returns>Throws a NotImplemented exception.</returns>
/// <returns>Throws a NotImplemented exception because we are in a non-interactive experience.</returns>
public override System.Security.SecureString ReadLineAsSecureString()
{
throw new NotImplementedException("The method or operation is not implemented.");
@ -161,7 +164,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// </summary>
public override void WriteLine()
{
//do nothing
//do nothing because we don't need to log empty lines
}
/// <summary>
@ -203,7 +206,6 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// <param name="message">The verbose message that is displayed.</param>
public override void WriteVerboseLine(string message)
{
//Console.WriteLine(String.Format(CultureInfo.CurrentCulture, "VERBOSE: {0}", message));
_logger.LogTrace(String.Format(CultureInfo.CurrentCulture, "VERBOSE: {0}", message));
}
@ -213,7 +215,6 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell.Host
/// <param name="message">The warning message that is displayed.</param>
public override void WriteWarningLine(string message)
{
//Console.WriteLine(String.Format(CultureInfo.CurrentCulture, "WARNING: {0}", message));
_logger.LogWarning(String.Format(CultureInfo.CurrentCulture, "WARNING: {0}", message));
}
}

Просмотреть файл

@ -0,0 +1,131 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Microsoft.Azure.Functions.PowerShellWorker.Utility;
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
namespace Microsoft.Azure.Functions.PowerShellWorker.PowerShell
{
using System.Management.Automation;
public static class PowerShellWorkerExtensions
{
// This script handles when the user adds something to the pipeline.
// It logs the item that comes and stores it as the $return out binding.
// The last item stored as $return will be returned to the function host.
private static string s_LogAndSetReturnValueScript = @"
param([Parameter(ValueFromPipeline=$true)]$return)
$return | Out-Default
Set-Variable -Name '$return' -Value $return -Scope global
";
public static PowerShell SetGlobalVariables(this PowerShell ps, Hashtable triggerMetadata, IList<ParameterBinding> inputData)
{
try {
// Set the global $Context variable which contains trigger metadata
ps.AddCommand("Set-Variable").AddParameters( new Hashtable {
{ "Name", "Context"},
{ "Scope", "Global"},
{ "Value", triggerMetadata}
}).Invoke();
// Sets a global variable for each input binding
foreach (ParameterBinding binding in inputData)
{
ps.AddCommand("Set-Variable").AddParameters( new Hashtable {
{ "Name", binding.Name},
{ "Scope", "Global"},
{ "Value", binding.Data.ToObject()}
}).Invoke();
}
return ps;
}
catch(Exception e)
{
ps.CleanupRunspace();
throw e;
}
}
public static PowerShell InvokeFunctionAndSetGlobalReturn(this PowerShell ps, string scriptPath, string entryPoint)
{
try
{
// We need to take into account if the user has an entry point.
// If it does, we invoke the command of that name
if(entryPoint != "")
{
ps.AddScript($@". {scriptPath}").Invoke();
ps.AddScript($@". {entryPoint}");
}
else
{
ps.AddScript($@". {scriptPath}");
}
// This script handles when the user adds something to the pipeline.
ps.AddScript(s_LogAndSetReturnValueScript).Invoke();
return ps;
}
catch(Exception e)
{
ps.CleanupRunspace();
throw e;
}
}
public static Hashtable ReturnBindingHashtable(this PowerShell ps, IDictionary<string, BindingInfo> outBindings)
{
try
{
// This script returns a hashtable that contains the
// output bindings that we will return to the function host.
var result = ps.AddScript(BuildBindingHashtableScript(outBindings)).Invoke<Hashtable>()[0];
ps.Commands.Clear();
return result;
}
catch(Exception e)
{
ps.CleanupRunspace();
throw e;
}
}
private static string BuildBindingHashtableScript(IDictionary<string, BindingInfo> outBindings)
{
// Since all of the out bindings are stored in variables at this point,
// we must construct a script that will return those output bindings in a hashtable
StringBuilder script = new StringBuilder();
script.AppendLine("@{");
foreach (KeyValuePair<string, BindingInfo> binding in outBindings)
{
script.Append("'");
script.Append(binding.Key);
// since $return has a dollar sign, we have to treat it differently
if (binding.Key == "$return")
{
script.Append("' = ");
}
else
{
script.Append("' = $");
}
script.AppendLine(binding.Key);
}
script.AppendLine("}");
return script.ToString();
}
// TODO: make sure this completely cleans up the runspace
private static void CleanupRunspace(this PowerShell ps)
{
ps.Commands.Clear();
}
}
}

Просмотреть файл

@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.Functions.PowerShellWorker.Requests
{
using System;
using System.Management.Automation;
using Microsoft.Azure.Functions.PowerShellWorker.Utility;
@ -15,20 +16,33 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Requests
RpcLogger logger)
{
FunctionLoadRequest functionLoadRequest = request.FunctionLoadRequest;
functionLoader.Load(functionLoadRequest.FunctionId, functionLoadRequest.Metadata);
var response = new StreamingMessage()
// Assume success unless something bad happens
StatusResult status = new StatusResult()
{
Status = StatusResult.Types.Status.Success
};
// Try to load the functions
try
{
functionLoader.Load(functionLoadRequest.FunctionId, functionLoadRequest.Metadata);
}
catch (Exception e)
{
status.Status = StatusResult.Types.Status.Failure;
status.Exception = e.ToRpcException();
}
return new StreamingMessage()
{
RequestId = request.RequestId,
FunctionLoadResponse = new FunctionLoadResponse()
{
FunctionId = functionLoadRequest.FunctionId,
Result = new StatusResult()
{
Status = StatusResult.Types.Status.Success
}
Result = status
}
};
return response;
}
}
}

Просмотреть файл

@ -4,6 +4,7 @@ using System.Collections.Generic;
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
using Microsoft.Azure.Functions.PowerShellWorker.Utility;
using Microsoft.Azure.Functions.PowerShellWorker.PowerShell;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.Functions.PowerShellWorker.Requests
@ -20,8 +21,22 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Requests
RpcLogger logger)
{
InvocationRequest invocationRequest = request.InvocationRequest;
// Set the RequestId and InvocationId for logging purposes
logger.SetContext(request.RequestId, invocationRequest.InvocationId);
// Load information about the function
var functionInfo = functionLoader.GetInfo(invocationRequest.FunctionId);
(string scriptPath, string entryPoint) = functionLoader.GetFunc(invocationRequest.FunctionId);
// Bundle all TriggerMetadata into Hashtable to send down to PowerShell
Hashtable triggerMetadata = new Hashtable();
foreach (var dataItem in invocationRequest.TriggerMetadata)
{
triggerMetadata.Add(dataItem.Key, dataItem.Value.ToObject());
}
// Assume success unless something bad happens
var status = new StatusResult() { Status = StatusResult.Types.Status.Success };
var response = new StreamingMessage()
{
@ -33,113 +48,34 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Requests
}
};
var info = functionLoader.GetInfo(invocationRequest.FunctionId);
// Add $Context variable, which contains trigger metadata, to the Global scope
Hashtable triggerMetadata = new Hashtable();
foreach (var dataItem in invocationRequest.TriggerMetadata)
{
triggerMetadata.Add(dataItem.Key, TypeConverter.FromTypedData(dataItem.Value));
}
if (triggerMetadata.Count > 0)
{
powershell.AddCommand("Set-Variable").AddParameters( new Hashtable {
{ "Name", "Context"},
{ "Scope", "Global"},
{ "Value", triggerMetadata}
});
powershell.Invoke();
}
foreach (ParameterBinding binding in invocationRequest.InputData)
{
powershell.AddCommand("Set-Variable").AddParameters( new Hashtable {
{ "Name", binding.Name},
{ "Scope", "Global"},
{ "Value", TypeConverter.FromTypedData(binding.Data)}
});
powershell.Invoke();
}
// foreach (KeyValuePair<string, BindingInfo> binding in info.OutputBindings)
// {
// powershell.AddCommand("Set-Variable").AddParameters( new Hashtable {
// { "Name", binding.Key},
// { "Scope", "Global"},
// { "Value", null}
// });
// powershell.Invoke();
// }
(string scriptPath, string entryPoint) = functionLoader.GetFunc(invocationRequest.FunctionId);
if(entryPoint != "")
{
powershell.AddScript($@". {scriptPath}");
powershell.Invoke();
powershell.AddCommand(entryPoint);
}
else
{
powershell.AddCommand(scriptPath);
}
powershell.AddScript(@"
param([Parameter(ValueFromPipeline=$true)]$return)
$return | Out-Default
Set-Variable -Name '$return' -Value $return -Scope global
");
StringBuilder script = new StringBuilder();
script.AppendLine("@{");
foreach (KeyValuePair<string, BindingInfo> binding in info.OutputBindings)
{
script.Append("'");
script.Append(binding.Key);
// since $return has a dollar sign, we have to treat it differently
if (binding.Key == "$return")
{
script.Append("' = ");
}
else
{
script.Append("' = $");
}
script.AppendLine(binding.Key);
}
script.AppendLine("}");
// Invoke powershell logic and return hashtable of out binding data
Hashtable result = null;
try
{
powershell.Invoke();
powershell.AddScript(script.ToString());
result = powershell.Invoke<Hashtable>()[0];
result = powershell
.SetGlobalVariables(triggerMetadata, invocationRequest.InputData)
.InvokeFunctionAndSetGlobalReturn(scriptPath, entryPoint)
.ReturnBindingHashtable(functionInfo.OutputBindings);
}
catch (Exception e)
{
status.Status = StatusResult.Types.Status.Failure;
status.Exception = TypeConverter.ToRpcException(e);
powershell.Commands.Clear();
status.Exception = e.ToRpcException();
return response;
}
powershell.Commands.Clear();
foreach (KeyValuePair<string, BindingInfo> binding in info.OutputBindings)
// Set out binding data and return response to be sent back to host
foreach (KeyValuePair<string, BindingInfo> binding in functionInfo.OutputBindings)
{
ParameterBinding paramBinding = new ParameterBinding()
{
Name = binding.Key,
Data = TypeConverter.ToTypedData(
result[binding.Key])
Data = result[binding.Key].ToTypedData()
};
response.InvocationResponse.OutputData.Add(paramBinding);
// if one of the bindings is $return we need to also set the ReturnValue
if(binding.Key == "$return")
{
response.InvocationResponse.ReturnValue = paramBinding.Data;

Просмотреть файл

@ -14,7 +14,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Requests
StreamingMessage request,
RpcLogger logger)
{
var response = new StreamingMessage()
return new StreamingMessage()
{
RequestId = request.RequestId,
WorkerInitResponse = new WorkerInitResponse()
@ -25,7 +25,6 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Requests
}
}
};
return response;
}
}
}

Просмотреть файл

@ -27,12 +27,6 @@ namespace Microsoft.Azure.Functions.PowerShellWorker
}
}
Console.WriteLine($"host: {arguments.Host}");
Console.WriteLine($"port: {arguments.Port}");
Console.WriteLine($"workerId: {arguments.WorkerId}");
Console.WriteLine($"requestId: {arguments.RequestId}");
Console.WriteLine($"grpcMaxMessageLength: {arguments.GrpcMaxMessageLength}");
return arguments;
}
}

Просмотреть файл

@ -42,7 +42,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Utility
RequestId = _requestId,
RpcLog = new RpcLog()
{
Exception = exception == null ? null : TypeConverter.ToRpcException(exception),
Exception = exception == null ? null : exception.ToRpcException(),
InvocationId = _invocationId,
Level = ConvertLogLevel(logLevel),
Message = formatter(state, exception)

Просмотреть файл

@ -7,24 +7,29 @@ using static Microsoft.Azure.WebJobs.Script.Grpc.Messages.TypedData;
using System;
using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
namespace Microsoft.Azure.Functions.PowerShellWorker.Utility
{
public class TypeConverter
public static class TypeExtensions
{
public static object ToObject (TypedData data)
public static object ToObject (this TypedData data)
{
if (data == null)
{
return null;
}
switch (data.DataCase)
{
case DataOneofCase.Json:
// consider doing ConvertFrom-Json
return data.Json;
return JsonConvert.DeserializeObject<Hashtable>(data.Json);
case DataOneofCase.Bytes:
return data.Bytes;
case DataOneofCase.Double:
return data.Double;
case DataOneofCase.Http:
return ToHttpContext(data.Http);
return data.Http.ToHttpContext();
case DataOneofCase.Int:
return data.Int;
case DataOneofCase.Stream:
@ -38,7 +43,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Utility
}
}
public static TypedData ToTypedData(object value)
public static TypedData ToTypedData(this object value)
{
TypedData typedData = new TypedData();
@ -55,7 +60,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Utility
else if(LanguagePrimitives.TryConvertTo<HttpResponseContext>(
value, out HttpResponseContext http))
{
typedData.Http = ToRpcHttp(http);
typedData.Http = http.ToRpcHttp();
}
else if (LanguagePrimitives.TryConvertTo<Hashtable>(
value, out Hashtable hashtable))
@ -65,6 +70,8 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Utility
else if (LanguagePrimitives.TryConvertTo<string>(
value, out string str))
{
// Attempt to parse the string into json. If it fails,
// fallback to storing as a string
try
{
typedData.Json = JsonConvert.SerializeObject(str);
@ -77,7 +84,7 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Utility
return typedData;
}
public static HttpRequestContext ToHttpContext (RpcHttp rpcHttp)
public static HttpRequestContext ToHttpContext (this RpcHttp rpcHttp)
{
var httpRequestContext = new HttpRequestContext
{
@ -91,35 +98,40 @@ namespace Microsoft.Azure.Functions.PowerShellWorker.Utility
if (rpcHttp.Body != null)
{
httpRequestContext.Body = ToObject(rpcHttp.Body);
httpRequestContext.Body = rpcHttp.Body.ToObject();
}
if (rpcHttp.RawBody != null)
{
httpRequestContext.Body = ToObject(rpcHttp.RawBody);
httpRequestContext.Body = rpcHttp.RawBody.ToObject();
}
return httpRequestContext;
}
public static RpcHttp ToRpcHttp (HttpResponseContext httpResponseContext)
public static RpcHttp ToRpcHttp (this HttpResponseContext httpResponseContext)
{
var rpcHttp = new RpcHttp
{
StatusCode = httpResponseContext.StatusCode?? "200"
StatusCode = httpResponseContext.StatusCode
};
if (httpResponseContext.Body != null)
{
rpcHttp.Body = httpResponseContext.Body;
rpcHttp.Body = httpResponseContext.Body.ToTypedData();
}
rpcHttp.Headers.Add(httpResponseContext.Headers);
// Add all the headers. ContentType is separated for convenience
foreach (DictionaryEntry item in httpResponseContext.Headers)
{
rpcHttp.Headers.Add(item.Key.ToString(), item.Value.ToString());
}
rpcHttp.Headers.Add("content-type", httpResponseContext.ContentType);
return rpcHttp;
}
public static RpcException ToRpcException (Exception exception)
public static RpcException ToRpcException (this Exception exception)
{
return new RpcException
{

Просмотреть файл

@ -9,7 +9,6 @@ using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
using Azure.Functions.PowerShell.Worker.Messaging;
using Microsoft.PowerShell;
using Microsoft.Azure.Functions.PowerShellWorker.Utility;
using System.Collections;
using Microsoft.Azure.Functions.PowerShellWorker.Requests;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.Functions.PowerShellWorker.PowerShell;
@ -33,10 +32,12 @@ namespace Microsoft.Azure.Functions.PowerShellWorker
}
StartupArguments startupArguments = StartupArguments.Parse(args);
// Initialize Rpc client, logger, and PowerShell
s_client = new FunctionMessagingClient(startupArguments.Host, startupArguments.Port);
s_Logger = new RpcLogger(s_client);
InitPowerShell();
// Send StartStream message
var streamingMessage = new StreamingMessage() {
RequestId = startupArguments.RequestId,
StartStream = new StartStream() { WorkerId = startupArguments.WorkerId }
@ -49,28 +50,21 @@ namespace Microsoft.Azure.Functions.PowerShellWorker
private static void InitPowerShell()
{
// var events = new StreamEvents(s_Logger);
var host = new Host(s_Logger);
var host = new AzureFunctionsHost(s_Logger);
s_runspace = RunspaceFactory.CreateRunspace(host);
s_runspace.Open();
s_ps = System.Management.Automation.PowerShell.Create(InitialSessionState.CreateDefault());
s_ps.Runspace = s_runspace;
// Setup Stream event listeners
// s_ps.Streams.Debug.DataAdded += events.DebugDataAdded;
// s_ps.Streams.Error.DataAdded += events.ErrorDataAdded;
// s_ps.Streams.Information.DataAdded += events.InformationDataAdded;
// s_ps.Streams.Progress.DataAdded += events.ProgressDataAdded;
// s_ps.Streams.Verbose.DataAdded += events.VerboseDataAdded;
// s_ps.Streams.Warning.DataAdded += events.WarningDataAdded;
s_ps.AddScript("$PSHOME");
//s_ps.AddCommand("Set-ExecutionPolicy").AddParameter("ExecutionPolicy", ExecutionPolicy.Unrestricted).AddParameter("Scope", ExecutionPolicyScope.Process);
var result = s_ps.Invoke<string>();
s_ps.Commands.Clear();
s_ps.Invoke<string>();
Console.WriteLine(result[0]);
// Add HttpResponseContext namespace so users can reference
// HttpResponseContext without needing to specify the full namespace
s_ps.AddScript($"using namespace {typeof(HttpResponseContext).Namespace}").Invoke();
s_ps.Commands.Clear();
}
private static async Task ProcessEvent()