Skip to content

Commit 72db50e

Browse files
Merge pull request #22506 from l0rd/win-install-hyperv
Add Hyper-V option in windows installer
2 parents 846d717 + fb4ddf8 commit 72db50e

File tree

8 files changed

+294
-47
lines changed

8 files changed

+294
-47
lines changed

.cirrus.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,11 @@ swagger_task:
483483

484484
win_installer_task:
485485
name: "Verify Win Installer Build"
486+
matrix:
487+
- env:
488+
CONTAINERS_MACHINE_PROVIDER: 'wsl'
489+
- env:
490+
CONTAINERS_MACHINE_PROVIDER: 'hyperv'
486491
alias: win_installer
487492
only_if: # RHEL never releases podman windows installer binary
488493
$CIRRUS_TAG == '' &&

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,13 @@ result
4444
# Necessary to prevent hack/tree-status.sh false-positive
4545
/*runner_stats.log
4646
.generate-bindings
47+
contrib/win-installer/artifacts/
48+
contrib/win-installer/docs/
49+
contrib/win-installer/fetch/
50+
contrib/win-installer/podman.msi
51+
contrib/win-installer/podman-*setup.exe
52+
contrib/win-installer/engine.exe
53+
contrib/win-installer/shasums
54+
contrib/win-installer/pages.wxs
55+
contrib/win-installer/*.wixobj
56+
contrib/win-installer/*.wixpdb

contrib/cirrus/win-installer-main.ps1

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,59 @@
22

33
. $PSScriptRoot\win-lib.ps1
44

5-
Set-Location "$ENV:CIRRUS_WORKING_DIR\repo\contrib\win-installer"
5+
if ($Env:CI -eq "true") {
6+
$WIN_INST_FOLDER = "$ENV:CIRRUS_WORKING_DIR\repo\contrib\win-installer"
7+
$RELEASE_DIR = "$ENV:CIRRUS_WORKING_DIR\repo"
8+
} else {
9+
$WIN_INST_FOLDER = "$PSScriptRoot\..\win-installer"
10+
$ENV:WIN_INST_VER = "9.9.9"
11+
$RELEASE_DIR = "$PSScriptRoot\..\.."
12+
$ENV:CONTAINERS_MACHINE_PROVIDER = "wsl"
13+
}
14+
15+
$ConfFilePath = "$env:ProgramData\containers\containers.conf.d\99-podman-machine-provider.conf"
16+
$WindowsPathsToTest = @("C:\Program Files\RedHat\Podman\podman.exe",
17+
"C:\Program Files\RedHat\Podman\win-sshproxy.exe",
18+
"$ConfFilePath",
19+
"HKLM:\SOFTWARE\Red Hat\Podman")
20+
21+
Set-Location $WIN_INST_FOLDER
622

723
# Build Installer
824
# Note: consumes podman-remote-release-windows_amd64.zip from repo.tbz2
9-
Run-Command ".\build.ps1 $Env:WIN_INST_VER dev `"$ENV:CIRRUS_WORKING_DIR\repo`""
25+
Run-Command ".\build.ps1 $Env:WIN_INST_VER dev `"$RELEASE_DIR`""
1026

11-
# Run the installer silently and WSL install option disabled (prevent reboots, wsl requirements)
27+
# Run the installer silently and WSL/HyperV install options disabled (prevent reboots)
1228
# We need AllowOldWin=1 for server 2019 (cirrus image), can be dropped after server 2022
13-
$ret = Start-Process -Wait -PassThru ".\podman-${ENV:WIN_INST_VER}-dev-setup.exe" -ArgumentList "/install /quiet WSLCheckbox=0 AllowOldWin=1 /log inst.log"
29+
$ret = Start-Process -Wait -PassThru ".\podman-${ENV:WIN_INST_VER}-dev-setup.exe" -ArgumentList "/install /quiet MachineProvider=$ENV:CONTAINERS_MACHINE_PROVIDER WSLCheckbox=0 HyperVCheckbox=0 AllowOldWin=1 /log inst.log"
1430
if ($ret.ExitCode -ne 0) {
1531
Write-Host "Install failed, dumping log"
1632
Get-Content inst.log
1733
throw "Exit code is $($ret.ExitCode)"
1834
}
19-
if (! ((Test-Path -Path "C:\Program Files\RedHat\Podman\podman.exe") -and `
20-
(Test-Path -Path "C:\Program Files\RedHat\Podman\win-sshproxy.exe"))) {
21-
throw "Expected podman.exe and win-sshproxy.exe, one or both not present after install"
35+
$WindowsPathsToTest | ForEach-Object {
36+
if (! (Test-Path -Path $_) ) {
37+
throw "Expected $_ but it's not present after uninstall"
38+
}
2239
}
40+
$machineProvider = Get-Content $ConfFilePath | Select-Object -Skip 1 | ConvertFrom-StringData | ForEach-Object { $_.provider }
41+
if ( $machineProvider -ne "`"$ENV:CONTAINERS_MACHINE_PROVIDER`"" ) {
42+
throw "Expected `"$ENV:CONTAINERS_MACHINE_PROVIDER`" as default machine provider but got $machineProvider"
43+
}
44+
2345
Write-Host "Installer verification successful!"
46+
47+
# Run the uninstaller silently to verify that it cleans up properly
48+
$ret = Start-Process -Wait -PassThru ".\podman-${ENV:WIN_INST_VER}-dev-setup.exe" -ArgumentList "/uninstall /quiet /log uninst.log"
49+
if ($ret.ExitCode -ne 0) {
50+
Write-Host "Uninstall failed, dumping log"
51+
Get-Content uninst.log
52+
throw "Exit code is $($ret.ExitCode)"
53+
}
54+
$WindowsPathsToTest | ForEach-Object {
55+
if ( Test-Path -Path $_ ) {
56+
throw "Path $_ is still present after uninstall"
57+
}
58+
}
59+
60+
Write-Host "Uninstaller verification successful!"

contrib/win-installer/burn.wxs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717
</BootstrapperApplicationRef>
1818
<Variable Name='InstallFolder' Type='string' Value='[ProgramFiles64Folder]RedHat\Podman' bal:Overridable="yes"/>
1919
<Variable Name="VERSION" Value="$(var.VERSION)"/>
20+
<Variable Name="MachineProvider" Type="string" Value="wsl" bal:Overridable="yes"/>
2021
<Variable Name="WSLCheckbox" Type="numeric" Value="1" bal:Overridable="yes"/>
22+
<Variable Name="HyperVCheckbox" Type="numeric" Value="0" bal:Overridable="yes"/>
2123
<Variable Name="AllowOldWin" Type="numeric" Value="0" bal:Overridable="yes"/>
2224
<Variable Name="LaunchTarget" Value="explorer.exe"/>
2325
<Variable Name="LaunchArguments" Value="&quot;[InstallFolder]\podman-for-windows.html&quot;"/>
26+
<Variable Name="SkipConfigFileCreation" Value="0"/>
2427

2528
<util:RegistrySearch Id="PreviousVersionSearch" Variable="PreviousVersion" Result="value" Root="HKLM" Key="SOFTWARE\[WixBundleManufacturer]\Updates\[WixBundleName]" Value="PackageVersion"/>
2629
<util:RegistrySearch Id="PreviousInstallFolderSearch" Root="HKLM" Key="SOFTWARE\[WixBundleManufacturer]\[WixBundleName]" Value="InstallDir" Variable="PreviousInstallFolder" Win64="yes"/>
@@ -35,9 +38,12 @@
3538
<Chain>
3639
<MsiPackage Id="Setup" SourceFile="podman.msi" Vital="yes">
3740
<MsiProperty Name="INSTALLDIR" Value="[InstallFolder]" />
41+
<MsiProperty Name="MACHINE_PROVIDER" Value="[MachineProvider]"/>
3842
<MsiProperty Name="WITH_WSL" Value="[WSLCheckbox]"/>
43+
<MsiProperty Name="WITH_HYPERV" Value="[HyperVCheckbox]"/>
44+
<MsiProperty Name="SKIP_CONFIG_FILE_CREATION" Value="[SkipConfigFileCreation]"/>
3945
</MsiPackage>
40-
<ExePackage DisplayName="WSL Kernel Install" InstallCondition="WSLCheckbox = 1" SourceFile="artifacts\podman-wslkerninst.exe"/>
46+
<ExePackage DisplayName="WSL Kernel Install" InstallCondition='(MachineProvider = "wsl") AND (WSLCheckbox = 1)' SourceFile="artifacts\podman-wslkerninst.exe"/>
4147
</Chain>
4248
<OptionalUpdateRegistration/>
4349
</Bundle>

contrib/win-installer/podman-msihooks/check.c

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
#include <MsiQuery.h>
33

44
BOOL isWSLEnabled();
5+
BOOL isHyperVEnabled();
56
LPCWSTR boolToNStr(BOOL bool);
7+
LPCSTR szSvcNameHyperv = TEXT("vmms");
68

79
/**
810
* CheckWSL is a custom action loaded by the Podman Windows installer
@@ -28,6 +30,19 @@ LPCWSTR boolToNStr(BOOL bool);
2830
return 0;
2931
}
3032

33+
/**
34+
* CheckHyperV is a custom action loaded by the Podman Windows installer
35+
* to determine whether the system already has Hyper-V installed.
36+
*/
37+
38+
__declspec(dllexport) UINT __cdecl CheckHyperV(MSIHANDLE hInstall) {
39+
BOOL hasHyperV = isHyperVEnabled();
40+
// Set a property with the HyperV state for the installer to operate on
41+
MsiSetPropertyW(hInstall, L"HAS_HYPERVFEATURE", boolToNStr(hasHyperV));
42+
43+
return 0;
44+
}
45+
3146
LPCWSTR boolToNStr(BOOL bool) {
3247
return bool ? L"1" : L"0";
3348
}
@@ -51,7 +66,7 @@ BOOL isWSLEnabled() {
5166
// CreateProcessW requires lpCommandLine to be mutable
5267
wchar_t cmd[] = L"wsl --set-default-version 2";
5368
if (! CreateProcessW(NULL, cmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE,
54-
NULL, NULL, &startup, &process)) {
69+
NULL, NULL, &startup, &process)) {
5570

5671
return FALSE;
5772
}
@@ -64,3 +79,52 @@ BOOL isWSLEnabled() {
6479

6580
return exitCode == 0;
6681
}
82+
83+
BOOL isHyperVEnabled() {
84+
/*
85+
* Checks if the Windows service `vmms` is running to
86+
* determine if Hyper-V is enabled.
87+
*/
88+
SC_HANDLE schSCManager;
89+
SC_HANDLE schService;
90+
SERVICE_STATUS_PROCESS ssStatus;
91+
DWORD dwBytesNeeded;
92+
93+
// Get a handle to the SCM database.
94+
schSCManager = OpenSCManager(
95+
NULL, // local computer
96+
NULL, // servicesActive database
97+
SERVICE_QUERY_STATUS); // service query access rights
98+
99+
if (NULL == schSCManager) {
100+
return FALSE;
101+
}
102+
103+
// Get a handle to the service.
104+
schService = OpenService(
105+
schSCManager,
106+
szSvcNameHyperv,
107+
SERVICE_QUERY_STATUS);
108+
109+
if (schService == NULL) {
110+
CloseServiceHandle(schSCManager);
111+
return FALSE;
112+
}
113+
114+
// Check the status
115+
if (!QueryServiceStatusEx(
116+
schService, // handle to service
117+
SC_STATUS_PROCESS_INFO, // information level
118+
(LPBYTE) &ssStatus, // address of structure
119+
sizeof(SERVICE_STATUS_PROCESS), // size of structure
120+
&dwBytesNeeded ) ) {
121+
CloseServiceHandle(schService);
122+
CloseServiceHandle(schSCManager);
123+
return FALSE;
124+
}
125+
126+
CloseServiceHandle(schService);
127+
CloseServiceHandle(schSCManager);
128+
129+
return ssStatus.dwCurrentState == SERVICE_RUNNING;
130+
}

contrib/win-installer/podman.wxs

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,83 @@
2020
<Media Id="1" Cabinet="Podman.cab" EmbedCab="yes"/>
2121
<MajorUpgrade AllowDowngrades="yes"/>
2222
<Property Id="DiskPrompt" Value="Red Hat's Podman $(var.VERSION) Installation"/>
23-
<SetProperty Id="WSL_INSTALL" Before="AppSearch" Value="1" Sequence="first">NOT (WITH_WSL = 0)</SetProperty>
23+
<Property Id="MACHINE_PROVIDER" Value="wsl"/>
24+
<Property Id="MACHINE_PROVIDER_CONFIG_FILE_PATH">
25+
<DirectorySearch Id="CommonAppDataFolderSearch" Path="[CommonAppDataFolder]">
26+
<DirectorySearch Id="ContainersFolderSearch" Path="containers">
27+
<DirectorySearch Id="ContainersConfDFolderSearch" Path="containers.conf.d">
28+
<FileSearch Name="99-podman-machine-provider.conf"/>
29+
</DirectorySearch>
30+
</DirectorySearch>
31+
</DirectorySearch>
32+
</Property>
33+
<Property Id="MAIN_EXECUTABLE_FILE_PATH">
34+
<DirectorySearch Id="ProgramFiles64FolderSearch" Path="[ProgramFiles64Folder]">
35+
<DirectorySearch Id="RedHatFolderSearch" Path="RedHat">
36+
<DirectorySearch Id="PodmanFolderSearch" Path="Podman">
37+
<FileSearch Name="podman.exe"/>
38+
</DirectorySearch>
39+
</DirectorySearch>
40+
</DirectorySearch>
41+
</Property>
42+
<!--
43+
Property WSL_INSTALL is set at runtime and used as the condition to run the `WSLFeatureComponent` Component:
44+
WSL is installed only if all these conditions are met:
45+
- WSL isn't already installed
46+
- The user has set property `MACHINE_PROVIDER` to "wsl"
47+
- The user hasn't set property `WITH_WSL` to 0
48+
-->
49+
<SetProperty Id="WSL_INSTALL" Before="AppSearch" Value="1" Sequence="first">
50+
<![CDATA[
51+
(HAS_WSLFEATURE = 0)
52+
AND (MACHINE_PROVIDER = "wsl")
53+
AND (NOT (WITH_WSL = 0))
54+
]]>
55+
</SetProperty>
56+
<!--
57+
Property HYPERV_INSTALL is set at runtime and used as the condition to run the `HyperVFeatureComponent` Component:
58+
HyperV is installed only if all these conditions are met:
59+
- HyperV isn't already installed
60+
- The user has set property `MACHINE_PROVIDER` to "hyperv"
61+
- The user hasn't set property `WITH_HYPERV` to 0
62+
-->
63+
<SetProperty Id="HYPERV_INSTALL" Before="AppSearch" Value="1" Sequence="first">
64+
<![CDATA[
65+
(HAS_HYPERVFEATURE = 0)
66+
AND (MACHINE_PROVIDER = "hyperv")
67+
AND (NOT (WITH_HYPERV = 0))
68+
]]>
69+
</SetProperty>
70+
<!--
71+
Property CREATE_MACHINE_PROVIDER_CONFIG_FILE is set at runtime and used as the condition to run the `MachineProviderConfigFile` Component:
72+
The machine provider config file is created only if all these conditions are met:
73+
- The user hasn't set property `SKIP_CONFIG_FILE_CREATION` to 1
74+
- The machine provider config file ($PROGRAMDATA/containers/containers.conf.d/99-podman-machine-provider.conf) doesn't exist
75+
- The main executable file ($PROGRAMDATA/RedHat/Podman/podman.exe) doesn't exist
76+
-->
77+
<SetProperty Id="CREATE_MACHINE_PROVIDER_CONFIG_FILE" After="AppSearch" Value="1" Sequence="first">
78+
<![CDATA[
79+
(NOT (SKIP_CONFIG_FILE_CREATION = 1))
80+
AND (NOT MACHINE_PROVIDER_CONFIG_FILE_PATH)
81+
AND (NOT MAIN_EXECUTABLE_FILE_PATH)
82+
]]>
83+
</SetProperty>
84+
<!--
85+
Property HIDE_PROVIDER_CHOICE is set at runtime and used as the condition to hide the Machine Provider
86+
choice from the MSI GUI (the Radio Button Group and other related controls):
87+
The machine provider choice isn't shown to the user if one of these conditions are met:
88+
- The user has set the property `SKIP_CONFIG_FILE_CREATION` to 1
89+
- The machine provider config file ($PROGRAMDATA/containers/containers.conf.d/99-podman-machine-provider.conf) exists
90+
- The main executable file ($PROGRAMDATA/RedHat/Podman/podman.exe) exists
91+
-->
92+
<SetProperty Id="HIDE_PROVIDER_CHOICE" After="AppSearch" Value="1" Sequence="first">
93+
<![CDATA[
94+
(SKIP_CONFIG_FILE_CREATION = 1)
95+
OR (MACHINE_PROVIDER_CONFIG_FILE_PATH)
96+
OR (MAIN_EXECUTABLE_FILE_PATH)
97+
]]>
98+
</SetProperty>
99+
24100
<Directory Id="TARGETDIR" Name="SourceDir">
25101
<Directory Id="ProgramFiles64Folder" Name="PFiles">
26102
<Directory Id="RedHatPFiles" Name="RedHat">
@@ -48,6 +124,22 @@
48124
</Directory>
49125
</Directory>
50126
</Directory>
127+
<!--
128+
The following code creates the `containers/containers.conf.d` folder under the system wide
129+
`$CommonAppDataFolder`. That's preferred to the user specific `$AppDataFolder` to avoid the
130+
Windows Installer ICE91 warning https://learn.microsoft.com/en-us/windows/win32/msi/ice91.
131+
-->
132+
<Directory Id="CommonAppDataFolder">
133+
<Directory Id="CONFIGDIR" Name="containers">
134+
<Directory Id="ContainersConfigSubDir" Name="containers.conf.d">
135+
<Component Id="MachineProviderConfigFile" Guid="C32C0040-D9AF-4155-AC7E-465B63B6BE3B">
136+
<CreateFolder />
137+
<IniFile Id="MachineProviderConfigFile" Action="createLine" Directory="ContainersConfigSubDir" Section="machine" Name="99-podman-machine-provider.conf" Key="provider" Value='"[MACHINE_PROVIDER]"' />
138+
<Condition>CREATE_MACHINE_PROVIDER_CONFIG_FILE</Condition>
139+
</Component>
140+
</Directory>
141+
</Directory>
142+
</Directory>
51143
<Directory Id="EnvEntries">
52144
<Component Id="EnvEntriesComponent" Guid="b662ec43-0e0e-4018-8bf3-061904bb8f5b" Win64="yes">
53145
<CreateFolder />
@@ -57,16 +149,24 @@
57149
</Directory>
58150

59151
<CustomAction Id="OpenGuide" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
60-
<CustomAction Id="CheckWSL" BinaryKey="PodmanHooks" Execute="immediate" DllEntry="CheckWSL" />
152+
<CustomAction Id="CheckWSL" BinaryKey="PodmanHooks" Execute="firstSequence" DllEntry="CheckWSL" />
153+
<CustomAction Id="CheckHyperV" BinaryKey="PodmanHooks" Execute="firstSequence" DllEntry="CheckHyperV" />
61154
<CustomActionRef Id="WixBroadcastEnvironmentChange" />
62155
<ComponentGroup Id="WSLFeature" Directory="INSTALLDIR">
63-
<Component>
64-
<Condition>(NOT Installed) AND WSL_INSTALL = 1 AND HAS_WSLFEATURE = 0</Condition>
65-
<File Source="$(sys.SOURCEFILEPATH)"/>
156+
<Component Id="WSLFeatureComponent" Guid="F6A693BC-186C-4E64-8015-C3073013B3A8">
157+
<Condition>(NOT Installed) AND WSL_INSTALL = 1</Condition>
158+
<CreateFolder />
66159
<PanelSW:Dism EnableFeature="VirtualMachinePlatform" ErrorHandling="prompt"/>
67160
<PanelSW:Dism EnableFeature="Microsoft-Windows-Subsystem-Linux" ErrorHandling="prompt"/>
68161
</Component>
69162
</ComponentGroup>
163+
<ComponentGroup Id="HyperVFeature" Directory="INSTALLDIR">
164+
<Component Id="HyperVFeatureComponent" Guid="F7B2D4C9-6C89-46BB-B4EA-FF39424972F3">
165+
<Condition>(NOT Installed) AND HYPERV_INSTALL = 1</Condition>
166+
<CreateFolder />
167+
<PanelSW:Dism EnableFeature="Microsoft-Hyper-V" ErrorHandling="prompt"/>
168+
</Component>
169+
</ComponentGroup>
70170
<Feature Id="Complete" Level="1">
71171
<ComponentRef Id="INSTALLDIR_Component"/>
72172
<ComponentRef Id="EnvEntriesComponent"/>
@@ -76,8 +176,10 @@
76176
<ComponentRef Id="GvProxyExecutable"/>
77177
<?endif?>
78178
<ComponentRef Id="GuideHTMLComponent"/>
179+
<ComponentRef Id="MachineProviderConfigFile"/>
79180
<ComponentGroupRef Id="ManFiles"/>
80181
<ComponentGroupRef Id="WSLFeature"/>
182+
<ComponentGroupRef Id="HyperVFeature"/>
81183
</Feature>
82184

83185
<Icon Id="podman.ico" SourceFile="resources/podman-logo.ico"/>
@@ -94,8 +196,13 @@
94196
</UI>
95197

96198
<InstallExecuteSequence>
97-
<Custom Action="CheckWSL" After="SetWSL_INSTALL">WSL_INSTALL = 1</Custom>
98-
<ForceReboot Before="StopServices">(NOT Installed) AND WSL_INSTALL = 1 AND HAS_WSLFEATURE = 0 AND NOT AFTERREBOOT</ForceReboot>
199+
<Custom Action="CheckWSL" Before="SetWSL_INSTALL" />
200+
<Custom Action="CheckHyperV" Before="SetHYPERV_INSTALL" />
201+
<ForceReboot Before="StopServices">
202+
(NOT Installed)
203+
AND ((WSL_INSTALL = 1) OR (HYPERV_INSTALL = 1))
204+
AND (NOT AFTERREBOOT)
205+
</ForceReboot>
99206
</InstallExecuteSequence>
100207
<Binary Id="PodmanHooks" SourceFile="artifacts/podman-msihooks.dll" />
101208
</Product>
897 Bytes
Loading

0 commit comments

Comments
 (0)