Skip to content

Update language worker to support parsing command-line arguments prefix with functions-<argumentname> #996

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

Merged
Merged
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
94 changes: 81 additions & 13 deletions src/Worker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,53 @@ public async static Task Main(string[] args)
LogLevel.Information,
string.Format(PowerShellWorkerStrings.PowerShellWorkerVersion, typeof(Worker).Assembly.GetName().Version));

WorkerArguments arguments = null;
Parser.Default.ParseArguments<WorkerArguments>(args)
.WithParsed(ops => arguments = ops)
.WithNotParsed(err => Environment.Exit(1));
var workerOptions = new WorkerOptions();

var parser = new Parser(settings =>
{
settings.EnableDashDash = true;
settings.IgnoreUnknownArguments = true;
});
parser.ParseArguments<WorkerArguments>(args)
.WithParsed(workerArgs =>
{
// TODO: Remove parsing old command-line arguments that are not prefixed with functions-<argumentname>
// for more information, see https://github.com/Azure/azure-functions-powershell-worker/issues/995
workerOptions.WorkerId = workerArgs.FunctionsWorkerId ?? workerArgs.WorkerId;
workerOptions.RequestId = workerArgs.FunctionsRequestId ?? workerArgs.RequestId;

if (!string.IsNullOrWhiteSpace(workerArgs.FunctionsUri))
{
try
{
// TODO: Update WorkerOptions to have a URI property instead of host name and port number
// for more information, see https://github.com/Azure/azure-functions-powershell-worker/issues/994
var uri = new Uri(workerArgs.FunctionsUri);
workerOptions.Host = uri.Host;
workerOptions.Port = uri.Port;
}
catch (UriFormatException formatEx)
{
var message = $"Invalid URI format: {workerArgs.FunctionsUri}. Error message: {formatEx.Message}";
throw new ArgumentException(message, nameof(workerArgs.FunctionsUri));
}
}
else
{
workerOptions.Host = workerArgs.Host;
workerOptions.Port = workerArgs.Port;
}

// Validate workerOptions
ValidateProperty("WorkerId", workerOptions.WorkerId);
ValidateProperty("RequestId", workerOptions.RequestId);
ValidateProperty("Host", workerOptions.Host);

if (workerOptions.Port <= 0)
{
throw new ArgumentException("Port number has not been initialized", nameof(workerOptions.Port));
}
});

// Create the very first Runspace so the debugger has the target to attach to.
// This PowerShell instance is shared by the first PowerShellManager instance created in the pool,
Expand All @@ -44,14 +87,14 @@ public async static Task Main(string[] args)
LogPowerShellVersion(pwshVersion);
WarmUpPowerShell(firstPowerShellInstance);

var msgStream = new MessagingStream(arguments.Host, arguments.Port);
var msgStream = new MessagingStream(workerOptions.Host, workerOptions.Port);
var requestProcessor = new RequestProcessor(msgStream, firstPowerShellInstance, pwshVersion);

// Send StartStream message
var startedMessage = new StreamingMessage()
{
RequestId = arguments.RequestId,
StartStream = new StartStream() { WorkerId = arguments.WorkerId }
RequestId = workerOptions.RequestId,
StartStream = new StartStream() { WorkerId = workerOptions.WorkerId }
};

msgStream.Write(startedMessage);
Expand Down Expand Up @@ -81,23 +124,48 @@ private static void LogPowerShellVersion(string pwshVersion)
var message = string.Format(PowerShellWorkerStrings.PowerShellVersion, pwshVersion);
RpcLogger.WriteSystemLog(LogLevel.Information, message);
}

private static void ValidateProperty(string name, string value)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException($"{name} is null or empty", name);
}
}
}

internal class WorkerArguments
{
[Option("host", Required = true, HelpText = "IP Address used to connect to the Host via gRPC.")]
[Option("host", Required = false, HelpText = "IP Address used to connect to the Host via gRPC.")]
public string Host { get; set; }

[Option("port", Required = true, HelpText = "Port used to connect to the Host via gRPC.")]
[Option("port", Required = false, HelpText = "Port used to connect to the Host via gRPC.")]
public int Port { get; set; }

[Option("workerId", Required = true, HelpText = "Worker ID assigned to this language worker.")]
[Option("workerId", Required = false, HelpText = "Worker ID assigned to this language worker.")]
public string WorkerId { get; set; }

[Option("requestId", Required = true, HelpText = "Request ID used for gRPC communication with the Host.")]
[Option("requestId", Required = false, HelpText = "Request ID used for gRPC communication with the Host.")]
public string RequestId { get; set; }

[Option("grpcMaxMessageLength", Required = false, HelpText = "[Deprecated and ignored] gRPC Maximum message size.")]
public int MaxMessageLength { get; set; }
[Option("functions-uri", Required = false, HelpText = "URI with IP Address and Port used to connect to the Host via gRPC.")]
public string FunctionsUri { get; set; }

[Option("functions-workerid", Required = false, HelpText = "Worker ID assigned to this language worker.")]
public string FunctionsWorkerId { get; set; }

[Option("functions-requestid", Required = false, HelpText = "Request ID used for gRPC communication with the Host.")]
public string FunctionsRequestId { get; set; }
}

internal class WorkerOptions
{
public string Host { get; set; }

public int Port { get; set; }

public string WorkerId { get; set; }

public string RequestId { get; set; }
}
}