-
Notifications
You must be signed in to change notification settings - Fork 7.7k
Update PowerShell Direct to try pwsh then fallback to powershell #7241
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
Update PowerShell Direct to try pwsh then fallback to powershell #7241
Conversation
…n PowerShell direct fixes PowerShell#7237
If I understand correctly, this partially resolves also #6669 but it also means this is a breaking change because the default is no longer Windows PowerShell? |
@bergmeister Yes, it is part of the resolution for the issue you mention. I've added the breaking change label for the reason you mentioned. |
// This code executes `powershell.exe` as it exists in the container which currently is | ||
// expected to be Windows PowerShell as it's inbox in the container. | ||
// This code executes `pwsh.exe` as it exists in the container which currently is | ||
// expected to be PowerShell Core as it's inbox in the container. | ||
// | ||
cmd = string.Format(System.Globalization.CultureInfo.InvariantCulture, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this logic be in a separate function?
} | ||
else | ||
{ | ||
processId = Convert.ToInt32(ProcessInformation.ProcessId); | ||
processId = 0; | ||
error = result; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should set ErrorMessage here and in the failure path for failing to launch powershell.exe; otherwise, it won't be diagnosable (i.e., which path failed? pwsh.exe or powershell.exe after not finding pwsh.exe)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subject to Dan's comments, looks ok to me.
make the code to get the process command a method make the error when we fail to launch the container process include the process that we were trying to launch.
error = result; | ||
processId = Convert.ToInt32(ProcessInformation.ProcessId); | ||
} | ||
else if (result == 2147942402) // 0x80070002 (2147942402) - The system cannot find the file specified. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer to just have the number on RHS in hex as it's easier to perform a web search against a hex error code. The comment can then be simplified.
Executable = "powershell.exe"; | ||
cmd = GetContainerProcessCommand(); | ||
|
||
result = HcsCreateProcess(ComputeSystem, cmd, ref ProcessInformation, ref Process, ref resultString); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this can be simplified as a loop (having only one HcsCreateProcess
and checking of the error). Have Executable
as an array containing pwsh.exe
, powershell.exe
. Once process creation succeeds, break out of the loop, otherwise try the next executable.
{ | ||
processId = 0; | ||
error = result; | ||
// the executable was found but did not work |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should return the HRESULT within the exception message for future troubleshooting
// the process was started, exit the loop. | ||
break; | ||
} | ||
else if (result == 0x80070002) // 0x80070002 (2147942402) - The system cannot find the file specified. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove the "0x80070002 (2147942402) -" as it's not needed now
return string.Format(System.Globalization.CultureInfo.InvariantCulture, | ||
@"{{""CommandLine"": ""{0} {1} -NoLogo {2}"",""RestrictedToken"": {3}}}", | ||
Executable, | ||
(RuntimeId != Guid.Empty) ? "-so -NoProfile" : "-NamedPipeServerMode", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we expand -so
?
/// </summary> | ||
private string GetContainerProcessCommand() | ||
{ | ||
return string.Format(System.Globalization.CultureInfo.InvariantCulture, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using string interpolation and FormattableString.Invariant would make this string construction easier to read:
using static System.FormattableString;
private string foo()
{
return Invariant($"bar {expression} {baz == null ? 1 : 0}");
}
(RuntimeId != Guid.Empty) ? "-so -NoProfile" : "-NamedPipeServerMode", | ||
String.IsNullOrEmpty(ConfigurationName) ? String.Empty : String.Concat("-Config ", ConfigurationName), | ||
(RunAsAdmin) ? "false" : "true"); | ||
string[] executables = new string[]("pwsh.exe"."powershell.exe") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am unfamiliar with this syntax. Shouldn't the string array initializer be?:
var executables = new string[] {"pwsh.exe", "powershell.exe"}
Also this can be a static readonly string array.
{ | ||
return string.Format(System.Globalization.CultureInfo.InvariantCulture, | ||
@"{{""CommandLine"": ""{0} {1} -NoLogo {2}"",""RestrictedToken"": {3}}}", | ||
Executable, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It feels cleaner if the Executable loop variable is passed in as a parameter. I know we need the property set for error handling, but this code is a bit confusing without realizing Executable is a loop variable.
// the process was started, exit the loop. | ||
break; | ||
} | ||
else if (result == 0x80070002) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we create a const variable for this expected error, with a descriptive name?
d6d49f1
to
cd90133
Compare
Also, set executable property in that function
cd90133
to
239f8a7
Compare
{ | ||
// "The system cannot find the file specified", try the next one | ||
// or exit the loop of none are left to try. | ||
continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If both pwsh.exe and powershell.exe files are not found then this will exit without error. It is unlikely but not impossible and I feel we should handle this error case so as not to change behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, it is actually fairly likely as NanoServer containers don't have PowerShell by default if they are 1709 or newer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Restarted CI due to #7260 |
002412f
to
b638ecd
Compare
@PowerShell/powershell-committee reviewed this and the order makes sense. There was some discussion about supporting pwsh-preview, however, we'll defer that until that is customer feedback requiring that change. |
PR Summary
Update PowerShell Direct to try pwsh then fallback to powershell
fixes #7237
PR Checklist
.h
,.cpp
,.cs
,.ps1
and.psm1
files have the correct copyright headerWIP:
to the beginning of the title and remove the prefix when the PR is ready.[feature]
if the change is significant or affects feature tests